diff --git a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/controller/AbstractExcelImportController.java b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/controller/AbstractExcelImportController.java index 89a5707c..11598002 100644 --- a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/controller/AbstractExcelImportController.java +++ b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/controller/AbstractExcelImportController.java @@ -1,48 +1,33 @@ package com.jmsoftware.maf.springcloudstarter.controller; import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; +import cn.hutool.poi.excel.ExcelReader; +import cn.hutool.poi.excel.WorkbookUtil; +import com.google.common.collect.Lists; import com.jmsoftware.maf.common.bean.ExcelImportResult; import com.jmsoftware.maf.common.bean.ResponseBodyBean; +import com.jmsoftware.maf.springcloudstarter.annotation.ExcelColumn; import com.jmsoftware.maf.springcloudstarter.configuration.ExcelImportConfiguration; -import com.jmsoftware.maf.springcloudstarter.sftp.SftpHelper; -import com.jmsoftware.maf.springcloudstarter.sftp.SftpUploadFile; -import com.jmsoftware.maf.springcloudstarter.util.PoiUtil; +import com.jmsoftware.maf.springcloudstarter.minio.MinioHelper; import io.swagger.annotations.ApiOperation; -import lombok.*; +import lombok.Cleanup; +import lombok.NonNull; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; -import org.apache.poi.ss.usermodel.*; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.springframework.boot.system.ApplicationHome; -import org.springframework.integration.file.support.FileExistsMode; -import org.springframework.util.Assert; -import org.springframework.util.ReflectionUtils; +import lombok.val; +import org.apache.poi.ss.usermodel.Workbook; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.multipart.MultipartHttpServletRequest; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; -import java.math.BigDecimal; -import java.text.SimpleDateFormat; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.util.*; +import java.util.List; /** *
After initializing, will upload Excel, read and bind data, validate data.
@@ -89,126 +73,36 @@ *Destroy locale context, in opposite of initLocaleContext()
.
- * Start with `check`, parameter is List list, return value is String or Boolean. - *
- * e.g public String checkName(List list)
- */
- private final Map Constructor of AbstractExcelImportController Init context. Call Set fieldNameArray Register handler methods Check if method is valid for validation. Method name starts with `check` Parameter is String value Return value is Boolean Init context. Call Set fieldNameArray
- * TODO: check the method if is necessary or not
- *
- * @param itemBytes the item bytes
- * @param fileName the file name
- * @return File file
- */
- private File uploadTempFile(byte[] itemBytes, String fileName) {
- val fileFullPath = TEMP_FILE_PATH + fileName;
- log.info("上传文件到本地(暂时)。文件绝对路径:{}", fileFullPath);
- // 新建文件
- val file = new File(fileFullPath);
- if (!file.getParentFile().exists()) {
- //noinspection ResultOfMethodCallIgnored
- file.getParentFile().mkdir();
- }
- // 上传
- // FileCopyUtils.copy(itemBytes, file);
- return file;
- }
-
- /**
- * Upload file.
- *
- * @param request the request
- * @return File file
- * @throws IOException the io exception
- * @see
- * Upload large files : Spring Boot
- */
- @SuppressWarnings("AlibabaRemoveCommentedCode")
- private File uploadFile(MultipartHttpServletRequest request) throws IOException {
- val multipartFile = request.getFileMap().get(FILE_KEY);
- // Don't do this.
- // it loads all of the bytes in java heap memory that leads to OutOfMemoryError. We'll use stream instead.
- // byte[] fileBytes = multipartFile.getBytes();
- @Cleanup val fileStream = new BufferedInputStream(multipartFile.getInputStream());
- LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
- val fileName = String.format("%s%s",
- LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")),
- multipartFile.getOriginalFilename());
- val targetFile = new File(TEMP_FILE_PATH + fileName);
- FileUtils.copyInputStreamToFile(fileStream, targetFile);
- uploadFileToSftp(targetFile);
- return targetFile;
- }
-
/**
* Read the file into workbook.
*
- * @param file the file
+ * @param multipartFile the multipart file
* @return the workbook
* @throws IOException the io exception
*/
- private Workbook readFile(@NonNull File file) throws IOException {
- Workbook workbook = null;
- val extension = FilenameUtils.getExtension(file.getName());
- val bufferedInputStream = new BufferedInputStream(new FileInputStream(file));
- if (XLS.equals(extension)) {
- POIFSFileSystem poifsFileSystem = new POIFSFileSystem(bufferedInputStream);
- workbook = new HSSFWorkbook(poifsFileSystem);
- bufferedInputStream.close();
- } else if (XLSX.equals(extension)) {
- workbook = new XSSFWorkbook(bufferedInputStream);
- }
- return workbook;
- }
-
- /**
- * Upload file to SFTP
- *
- * @param file the file
- */
- private void uploadFileToSftp(File file) {
- val sftpUploadFile = SftpUploadFile.builder()
- .fileToBeUploaded(file)
- .fileExistsMode(FileExistsMode.REPLACE)
- .subDirectory(SFTP_DIR).build();
- try {
- sftpHelper.upload(sftpUploadFile);
- } catch (Exception e) {
- log.error("Exception occurred when uploading file to SFTP! Exception message:{}", e.getMessage());
- }
+ private Workbook readFile(@NonNull MultipartFile multipartFile) throws IOException {
+ return WorkbookUtil.createBook(multipartFile.getInputStream());
}
- /**
- * Filter sheet. In default, will proceed all sheets.
- *
- * @param sheet the sheet
- * @return the boolean
- */
- protected boolean filterSheet(Sheet sheet) {
- return true;
+ @SneakyThrows
+ private void uploadWorkbook() {
+ @Cleanup val outputStream = new ByteArrayOutputStream();
+ this.workbook.get().write(outputStream);
+ final var bufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(outputStream.toByteArray()));
+ this.minioHelper.makeBucket("temp");
+ this.minioHelper.putObject("temp", this.fileName.get(), bufferedInputStream,
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+ this.excelFilePath.set("temp/temp.xlsx");
}
/**
@@ -637,464 +346,33 @@ protected boolean filterSheet(Sheet sheet) {
private void bindData(Workbook workbook) throws Exception {
for (int index = 0; index < workbook.getNumberOfSheets(); index++) {
val sheet = workbook.getSheetAt(index);
- if (!filterSheet(sheet)) {
- continue;
- }
- // Specify current sheet location
- sheetLocation.set(index + 1);
- sheet.getSheetName();
- // Set Reading row count
- readingRowCount.set(readingRowCount.get() + sheet.getLastRowNum() - sheet.getFirstRowNum());
- // Check if exceeding the MAXIMUM_ROW_COUNT
- if (readingRowCount.get() > excelImportConfiguration.getMaximumRowCount()) {
- setErrorMessage(String.format(
- "The amount of importing data cannot be greater than %d (Table head included)! " +
- "Current reading row count: %d", excelImportConfiguration.getMaximumRowCount(),
- readingRowCount.get()));
- continue;
- }
- // Check if the readingRowCount is equal to zero
- if (readingRowCount.get() == 0) {
- setErrorMessage("Not available data to import. Check Excel again.");
- continue;
- }
- // Check if the first row is equal to null
- if (sheet.getRow(0) == null) {
- setErrorMessage(String.format("Sheet [%s] (No. %d) format is invalid: no title! ", sheet.getSheetName(),
- sheetLocation.get()));
- // If the sheet is not valid, then skip it
- continue;
- }
- val startIndex = sheet.getRow(0).getFirstCellNum();
- val endIndex = sheet.getRow(0).getLastCellNum();
- // Parse from the second row
- for (var i = sheet.getFirstRowNum() + 1; i <= sheet.getLastRowNum(); i++) {
- // Specify current row location
- rowLocation.set(i + 1);
- val row = sheet.getRow(i);
- if (isBlankRow(row)) {
- // Blank row will not be considered as a effective row, not be included in `readingRowCount`
- readingRowCount.set(readingRowCount.get() - 1);
- continue;
- }
- // Bind row to bean
- bindRowToBean(row, startIndex, endIndex);
+ val excelReader = new ExcelReader(workbook, index);
+ Field[] declaredFields = this.bindClass.getDeclaredFields();
+ for (Field declaredField : declaredFields) {
+ ExcelColumn annotation = declaredField.getAnnotation(ExcelColumn.class);
+ excelReader.addHeaderAlias(annotation.description(), declaredField.getName());
}
- }
- }
-
- /**
- * Check if the row is blank. If there is one cell of the row is not blank, then the row is not blank.
- *
- * @param row the row
- * @return true if the row is blank; or vice versa.
- */
- private boolean isBlankRow(Row row) {
- if (row == null) {
- return true;
- }
- Cell cell;
- var value = "";
- for (var i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
- cell = row.getCell(i, Row.MissingCellPolicy.RETURN_BLANK_AS_NULL);
- if (cell != null) {
- switch (cell.getCellType()) {
- case STRING:
- value = cell.getStringCellValue().trim();
- break;
- case NUMERIC:
- value = String.valueOf((int) cell.getNumericCellValue());
- break;
- case BOOLEAN:
- value = String.valueOf(cell.getBooleanCellValue());
- break;
- case FORMULA:
- value = String.valueOf(cell.getCellFormula());
- break;
- default:
- break;
- }
- if (StrUtil.isNotBlank(value)) {
- return false;
- }
+ this.beanList.set(excelReader.readAll(this.bindClass));
+ for (var beanIndex = 0; beanIndex < this.beanList.get().size(); beanIndex++) {
+ this.validateBeanByRow(index, beanIndex);
}
}
- return true;
}
- /**
- * Bind row to bean.
- *
- * @param row the row
- * @param startIndex the start index
- * @param endIndex the end index
- */
- private void bindRowToBean(Row row, int startIndex, int endIndex) {
- Assert.notNull(this.bindClass, "bindClass must not be null!");
- Assert.notNull(this.fieldNameArray, "fieldNameArray must not be null!");
- var bindingResult = false;
+ private void validateBeanByRow(int sheetIndex, int beanIndex) {
try {
- // New bean instance
- val bean = this.bindClass.getDeclaredConstructor().newInstance();
- val beanInfo = Introspector.getBeanInfo(bean.getClass());
- val propertyDescriptors = beanInfo.getPropertyDescriptors();
- for (var index = startIndex; index < endIndex; index++) {
- // If found more data, then binding failed
- if (index >= fieldNameArray.length) {
- bindingResult = false;
- setErrorMessage(
- String.format("Found redundant data on row number %d. Check again.", rowLocation.get()));
- break;
- }
- // Get the field that needs binding
- val fieldName = fieldNameArray[index];
- PropertyDescriptor propertyDescriptor = null;
- for (val pd : propertyDescriptors) {
- if (pd.getName().equals(fieldName)) {
- propertyDescriptor = pd;
- break;
- }
- }
- if (ObjectUtil.isNull(propertyDescriptor)) {
- throw new RuntimeException("Cannot find the field in the specify class!");
- }
- val value = getCellValue2String(row.getCell(index));
- // Start to bind
- // Specify current column location
- columnLocation.set(index + 1);
- // Execute custom validation method
- val customValidationMethod = this.checkMethodMap.get(fieldName);
- Object returnObj = null;
- if (ObjectUtil.isNotNull(customValidationMethod)) {
- returnObj = customValidationMethod.invoke(this, value, rowLocation.get(), bean);
- }
- val validationResult = returnObj == null ? null : returnObj.toString();
- // If `validationResult` is blank or equal to "true"
- if (StrUtil.isBlank(validationResult) || Boolean.TRUE.toString().equals(validationResult)) {
- bindingResult = bind(value, propertyDescriptor, bean);
- } else {
- bindingResult = false;
- // If `validationResult` is not equal to "false" then add to error message list
- if (!Boolean.FALSE.toString().equals(validationResult)) {
- setErrorMessage(validationResult);
- }
- }
- // Finished binding
- if (!bindingResult) {
- break;
- }
- }
- if (bindingResult) {
- bindingResult = validateBeforeAddToBeanList(this.beanList.get(), bean, rowLocation.get());
- }
- if (bindingResult) {
- this.beanList.get().add(bean);
- }
- } catch (IntrospectionException
- | InvocationTargetException
- | InstantiationException
- | IllegalAccessException
- | NoSuchMethodException e) {
+ this.validateBeforeAddToBeanList(this.beanList.get(), this.beanList.get().get(beanIndex), beanIndex);
+ } catch (Exception e) {
log.error("bindRowToBean method has encountered a problem!", e);
- exceptionOccurred.set(true);
+ this.exceptionOccurred.set(true);
val errorMessage = String.format(
"Exception occurred when binding and validating the data of row number %d. " +
- "Exception message: %s", rowLocation.get(), e.getMessage());
- setErrorMessage(errorMessage);
- val lastCellNum = row.getLastCellNum();
- val errorInformationCell = row.createCell(fieldNameArray.length);
+ "Exception message: %s", beanIndex + 1, e.getMessage());
+ this.setErrorMessage(errorMessage);
+ val errorRow = this.workbook.get().getSheetAt(sheetIndex).getRow(beanIndex + 1);
+ val errorInformationCell = errorRow.createCell(this.fieldNameArray.length);
errorInformationCell.setCellValue(errorMessage);
- val firstSheet = workbookWithErrorMessage.get().getSheetAt(0);
- val row1 = firstSheet.createRow(firstSheet.getLastRowNum() + 1);
- PoiUtil.copyRow(true, workbookWithErrorMessage.get(), row, row1);
- }
- }
-
- /**
- * Gets cell value 2 string.
- *
- * @param cell the cell
- * @return the string
- */
- private String getCellValue2String(Cell cell) {
- var returnString = "";
- if (ObjectUtil.isNull(cell)) {
- return returnString;
- }
- switch (cell.getCellType()) {
- case BLANK:
- return "";
- case NUMERIC:
- // If it's date
- if (DateUtil.isCellDateFormatted(cell)) {
- val date = cell.getDateCellValue();
- val dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- returnString = dateFormat.format(date);
- return returnString;
- }
- val bigDecimal = new BigDecimal(String.valueOf(cell.getNumericCellValue()).trim());
- // Keep decimal fraction parts which are not zero
- val tempStr = bigDecimal.toPlainString();
- val dotIndex = tempStr.indexOf(".");
- if ((bigDecimal.doubleValue() - bigDecimal.longValue()) == 0 && dotIndex > 0) {
- returnString = tempStr.substring(0, dotIndex);
- } else {
- returnString = tempStr;
- }
- return returnString;
- case STRING:
- if (cell.getStringCellValue() != null) {
- returnString = cell.getStringCellValue().trim();
- }
- return returnString;
- default:
- return returnString;
- }
- }
-
- /**
- * Register bind handler methods.
- */
- private void registerBindHandlerMethods() {
- try {
- val bindStringField = ReflectionUtils.findMethod(this.getClass(),
- "bindStringField",
- String.class,
- PropertyDescriptor.class,
- Object.class);
- val bindIntField = ReflectionUtils.findMethod(this.getClass(),
- "bindIntField",
- String.class,
- PropertyDescriptor.class,
- Object.class);
- val bindLongField = ReflectionUtils.findMethod(this.getClass(),
- "bindLongField",
- String.class,
- PropertyDescriptor.class,
- Object.class);
- val bindFloatField = ReflectionUtils.findMethod(this.getClass(),
- "bindFloatField",
- String.class,
- PropertyDescriptor.class,
- Object.class);
- val bindDoubleField = ReflectionUtils.findMethod(this.getClass(),
- "bindDoubleField",
- String.class,
- PropertyDescriptor.class,
- Object.class);
- val bindLocalDateTimeField = ReflectionUtils.findMethod(this.getClass(),
- "bindLocalDateTimeField",
- String.class,
- PropertyDescriptor.class,
- Object.class);
- this.bindMethodMap.put(String.class, bindStringField);
- this.bindMethodMap.put(Integer.class, bindIntField);
- this.bindMethodMap.put(Long.class, bindLongField);
- this.bindMethodMap.put(Float.class, bindFloatField);
- this.bindMethodMap.put(Double.class, bindDoubleField);
- this.bindMethodMap.put(LocalDateTime.class, bindLocalDateTimeField);
- } catch (Exception e) {
- log.error("The bindMethod required was not found in this class!", e);
- }
- }
-
- /**
- * Real binding value to the bean's field.
- *
- *
- * @param value the string value of the excel cell
- * @param propertyDescriptor the the field of the bean
- * @param bean the bean
- * @return true if the binding succeeded, or vice versa.
- * @throws RuntimeException the runtime exception
- */
- private Boolean bind(String value, PropertyDescriptor propertyDescriptor, Object bean) throws RuntimeException {
- boolean result;
- val fieldType = propertyDescriptor.getPropertyType();
- if (!this.bindMethodMap.containsKey(fieldType)) {
- throw new RuntimeException(
- String.format("The bindMethod required was not found in bindMethodMap! Field type: %s",
- fieldType.getSimpleName()));
- }
- val bindMethod = this.bindMethodMap.get(fieldType);
- value = StrUtil.isBlank(value) ? null : value;
- try {
- // Why is the parameter 'this' required? Because it's this class calling the 'bindMethod',
- // and the 'bingMethod' belongs to this class object.
- result = (Boolean) bindMethod.invoke(this, value, propertyDescriptor, bean);
- } catch (IllegalAccessException
- | IllegalArgumentException
- | InvocationTargetException e) {
- val exceptionMessage = new StringBuilder("Exception occurred when binding! ");
- if (!StrUtil.isBlank(e.getMessage())) {
- log.error("Exception occurred when invoking method!", e);
- exceptionMessage.append(e.getMessage()).append(" ");
- }
- if (ObjectUtil.isNotNull(e.getCause()) && !StrUtil.isBlank(e.getCause().getMessage())) {
- log.error("Exception occurred when invoking method!", e.getCause());
- exceptionMessage.append(e.getCause().getMessage());
- }
- throw new RuntimeException(exceptionMessage.toString());
- }
- return result;
- }
-
- /**
- * Bind int field boolean.
- *
- * @param value the value
- * @param propertyDescriptor the property descriptor
- * @param bean the bean
- * @return the boolean
- * @throws IllegalAccessException the illegal access exception
- */
- private Boolean bindIntField(String value, PropertyDescriptor propertyDescriptor, Object bean) throws IllegalAccessException {
- try {
- var realValue = value == null ? null : Integer.parseInt(value);
- if (ObjectUtil.isNull(realValue) && propertyDescriptor.getPropertyType() == int.class) {
- realValue = 0;
- }
- propertyDescriptor.getWriteMethod().invoke(bean, realValue);
- } catch (NumberFormatException | InvocationTargetException e) {
- log.error("Exception occurred when binding int/Integer field! Exception message: {}, value: {}, field: {}",
- e.getMessage(), value, propertyDescriptor.getName());
- val formattedMessage = String.format("Invalid data of the row %d, col %d, must be integer",
- rowLocation.get(), columnLocation.get());
- setErrorMessage(formattedMessage);
- throw new IllegalArgumentException(formattedMessage);
- }
- return true;
- }
-
- /**
- * Bind long field boolean.
- *
- * @param value the value
- * @param propertyDescriptor the property descriptor
- * @param bean the bean
- * @return the boolean
- * @throws IllegalAccessException the illegal access exception
- */
- private Boolean bindLongField(String value, PropertyDescriptor propertyDescriptor, Object bean) throws IllegalAccessException {
- try {
- var realValue = value == null ? null : (long) Double.parseDouble(value);
- if (ObjectUtil.isNull(realValue) && propertyDescriptor.getPropertyType() == long.class) {
- realValue = 0L;
- }
- propertyDescriptor.getWriteMethod().invoke(bean, realValue);
- } catch (NumberFormatException | InvocationTargetException e) {
- log.error("Exception occurred when binding long/Long field! Exception message: {}, value: {}, field: {}",
- e.getMessage(), value, propertyDescriptor.getName());
- val formattedMessage = String.format("Invalid data of the row %d, col %d, must be long integer",
- rowLocation.get(), columnLocation.get());
- setErrorMessage(formattedMessage);
- throw new IllegalArgumentException(formattedMessage);
- }
- return true;
- }
-
- /**
- * Bind float field boolean.
- *
- * @param value the value
- * @param propertyDescriptor the property descriptor
- * @param bean the bean
- * @return the boolean
- * @throws IllegalAccessException the illegal access exception
- */
- private Boolean bindFloatField(String value, PropertyDescriptor propertyDescriptor, Object bean) throws IllegalAccessException {
- try {
- var realValue = value == null ? null : Float.parseFloat(value);
- if (ObjectUtil.isNull(realValue) && propertyDescriptor.getPropertyType() == float.class) {
- realValue = 0F;
- }
- propertyDescriptor.getWriteMethod().invoke(bean, realValue);
- } catch (NumberFormatException | InvocationTargetException e) {
- log.error("Exception occurred when binding float/Float field! Exception message: {}, value: {}, field: {}",
- e.getMessage(), value, propertyDescriptor.getName());
- val formattedMessage = String.format("Invalid data of the row %d, col %d, must be float",
- rowLocation.get(), columnLocation.get());
- setErrorMessage(formattedMessage);
- throw new IllegalArgumentException(formattedMessage);
- }
- return true;
- }
-
- /**
- * Bind double field boolean.
- *
- * @param value the value
- * @param propertyDescriptor the property descriptor
- * @param bean the bean
- * @return the boolean
- * @throws IllegalAccessException the illegal access exception
- */
- private Boolean bindDoubleField(String value, PropertyDescriptor propertyDescriptor, Object bean) throws IllegalAccessException {
- try {
- var realValue = value == null ? null : Double.parseDouble(value);
- if (ObjectUtil.isNull(realValue) && propertyDescriptor.getPropertyType() == double.class) {
- realValue = 0D;
- }
- propertyDescriptor.getWriteMethod().invoke(bean, realValue);
- } catch (NumberFormatException | InvocationTargetException e) {
- log.error(
- "Exception occurred when binding double/Double field! Exception message: {}, value: {}, field: {}",
- e.getMessage(), value, propertyDescriptor.getName());
- val formattedMessage = String.format("Invalid data of the row %d, col %d, must be double",
- rowLocation.get(), columnLocation.get());
- setErrorMessage(formattedMessage);
- throw new IllegalArgumentException(formattedMessage);
}
- return true;
- }
-
- /**
- * Bind string field boolean.
- *
- * @param value the value
- * @param propertyDescriptor the property descriptor
- * @param bean the bean
- * @return the boolean
- * @throws IllegalAccessException the illegal access exception
- */
- private Boolean bindStringField(String value, PropertyDescriptor propertyDescriptor, Object bean) throws IllegalAccessException {
- try {
- propertyDescriptor.getWriteMethod().invoke(bean, value);
- } catch (InvocationTargetException e) {
- log.error("Exception occurred when binding String field! Exception message: {}, value: {}, field: {}",
- e.getMessage(), value, propertyDescriptor.getName());
- val formattedMessage = String.format("Invalid data of the row %d, col %d, must be string",
- rowLocation.get(), columnLocation.get());
- setErrorMessage(formattedMessage);
- throw new IllegalArgumentException(formattedMessage);
- }
- return true;
- }
-
- /**
- * Bind local date time field boolean.
- *
- * @param value the value
- * @param propertyDescriptor the property descriptor
- * @param bean the bean
- * @return the boolean
- * @throws IllegalAccessException the illegal access exception
- */
- private Boolean bindLocalDateTimeField(String value, PropertyDescriptor propertyDescriptor, Object bean) throws IllegalAccessException {
- try {
- val date = value == null ? null : LocalDateTime.parse(value,
- DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
- propertyDescriptor.getWriteMethod().invoke(bean, date);
- } catch (DateTimeParseException | InvocationTargetException e) {
- log.error(
- "Exception occurred when binding LocalDateTime field! Exception message: {}, value: {}, field: {}",
- e.getMessage(), value, propertyDescriptor.getName());
- val formattedMessage = String.format("Invalid data of the row %d, col %d, must be date",
- rowLocation.get(), columnLocation.get());
- setErrorMessage(formattedMessage);
- throw new IllegalArgumentException(formattedMessage);
- }
- return true;
}
/**
@@ -1113,7 +391,7 @@ public void setDenyAll(Boolean denyAll) {
*/
protected void setErrorMessage(String errorInfo) {
this.errorMessageList.get().add(errorInfo);
- setReturnMessageList(errorInfo);
+ this.setReturnMessageList(errorInfo);
}
/**
@@ -1124,4 +402,20 @@ protected void setErrorMessage(String errorInfo) {
protected void setReturnMessageList(String message) {
this.returnMessageList.get().add(message);
}
+
+ /**
+ * Close workbook.
+ *
+ * @param workbook the workbook
+ */
+ private void closeWorkbook(Workbook workbook) {
+ if (workbook != null) {
+ try {
+ workbook.close();
+ } catch (IOException e) {
+ log.error("Exception occurred when closing workbook! Exception message: {}, workbook: {}",
+ e.getMessage(), workbook);
+ }
+ }
+ }
}
> errorMessageList = new ThreadLocal<>();
- /**
- * Bean list. After reading Excel
- */
- protected final ThreadLocal
> beanList = new ThreadLocal<>();
- /**
- * Return message list
- */
- private final ThreadLocal
> returnMessageList = new ThreadLocal<>();
- /**
- * The Workbook.
- */
- protected final ThreadLocal
+ *
+ */
+ protected void initContext() {
+ this.bindClass = this.getGenericClass();
+ val declaredFields = this.bindClass.getDeclaredFields();
+ val fieldNames = new String[declaredFields.length];
+ for (var index = 0; index < declaredFields.length; index++) {
+ val declaredField = declaredFields[index];
+ fieldNames[index] = declaredField.getName();
+ }
+ log.info("Generated {} field name array by reflection, fieldNames: {}", this.bindClass.getSimpleName(),
+ fieldNames);
+ this.fieldNameArray = fieldNames;
}
/**
* Init locale context.
*/
private void initLocaleContext() {
- errorMessageList.set(new LinkedList<>());
- beanList.set(new LinkedList<>());
- returnMessageList.set(new LinkedList<>());
- rowLocation.set(0);
- columnLocation.set(0);
- sheetLocation.set(0);
- readingRowCount.set(0);
- userDefinedMessage.set("");
- exceptionOccurred.set(false);
+ this.beanList.set(Lists.newLinkedList());
+ this.errorMessageList.set(Lists.newLinkedList());
+ this.returnMessageList.set(Lists.newLinkedList());
+ this.exceptionOccurred.set(false);
+ }
+
+ /**
+ * Destroy locale context.
+ */
+ private void destroyLocaleContext() {
+ this.beanList.remove();
+ this.closeWorkbook(this.workbook.get());
+ this.workbook.remove();
+ this.excelFilePath.remove();
+ this.exceptionOccurred.remove();
+ this.errorMessageList.remove();
+ this.returnMessageList.remove();
+ this.fileName.remove();
+ }
+
+ /**
+ * Before execute.
+ */
+ protected void beforeExecute() {
}
/**
@@ -248,273 +177,128 @@ private void initLocaleContext() {
protected abstract void onExceptionOccurred();
/**
- * Destroy locale context.
+ * Validate before adding to bean list boolean.
+ *
+ * @param beanList the bean list that contains validated bean
+ * @param bean the bean that needs to be validated
+ * @param index the index that is the reference to the row number of the Excel file
+ * @return the boolean
+ * @throws IllegalArgumentException the illegal argument exception
*/
- private void destroyLocaleContext() {
- errorMessageList.remove();
- beanList.remove();
- returnMessageList.remove();
- rowLocation.remove();
- columnLocation.remove();
- sheetLocation.remove();
- readingRowCount.remove();
- userDefinedMessage.remove();
- closeWorkbook(workbook.get());
- workbook.remove();
- closeWorkbook(workbookWithErrorMessage.get());
- workbookWithErrorMessage.remove();
- excelFilePath.remove();
- exceptionOccurred.remove();
- file.remove();
+ protected abstract boolean validateBeforeAddToBeanList(ListgetGenericClass()
to set Excel import type;
- *
- */
- private void registerHandlerMethods() {
- this.checkMethodMap.clear();
- // Register valid methods for validation
- Method[] methods = this.getClass().getDeclaredMethods();
- for (Method method : methods) {
- if (isCheckMethod(method)) {
- registerCheckMethod(method);
- }
- }
- }
-
- /**
- * private Boolean checkXXX(String value,Integer index)
private String checkXXX(String value,Integer index)
, the return value can be empty or
- * “true”, or other error message which will be added to errorMessage
- *
- *
- * @param method the method
- * @return the boolean
- */
- private Boolean isCheckMethod(Method method) {
- val returnType = method.getReturnType();
- if (method.getName().startsWith(DEFAULT_CHECK_METHOD_PREFIX)) {
- if (String.class.equals(returnType) || Boolean.class.equals(returnType) || boolean.class.equals(
- returnType)) {
- Class>[] parameterTypes = method.getParameterTypes();
- return parameterTypes.length >= 2 && String.class.equals(parameterTypes[0])
- && (int.class.equals(parameterTypes[1]) || Integer.class.equals(parameterTypes[1]));
- }
- }
- return false;
- }
-
- /**
- * Register check method.
- *
- * @param method the method
- */
- private void registerCheckMethod(Method method) {
- var fieldName = method.getName().
- substring(DEFAULT_CHECK_METHOD_PREFIX.length());
- fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
- this.checkMethodMap.put(fieldName, method);
- log.info("Registered check method [" + method.getName() + "]");
- }
-
- /**
- *
- *
- */
- protected void initContext() {
- bindClass = this.getGenericClass();
- val declaredFields = bindClass.getDeclaredFields();
- val fieldNameArray = new String[declaredFields.length];
- for (var index = 0; index < declaredFields.length; index++) {
- val declaredField = declaredFields[index];
- fieldNameArray[index] = declaredField.getName();
- }
- log.info("Generated {} field name array by reflection, fieldNameArray: {}", bindClass.getSimpleName(),
- fieldNameArray);
- this.setFieldNameArray(fieldNameArray);
- }
-
/**
* Gets generic class.
*
* @return the generic class
*/
- private ClassgetGenericClass()
to set Excel import type;