From f99c198df7601e6967f425008aacdb886c6be63d Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Mon, 16 Mar 2020 02:07:18 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B5=8B=E8=AF=95bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../excel/analysis/ExcelAnalyserImpl.java | 3 +- .../excel/analysis/v03/XlsSaxAnalyser.java | 12 +- .../v03/handlers/BofRecordHandler.java | 6 +- .../v03/handlers/DummyRecordHandler.java | 2 + .../v03/handlers/FormulaRecordHandler.java | 7 + .../analysis/v07/handlers/RowTagHandler.java | 19 ++- .../read/builder/ExcelReaderBuilder.java | 2 +- .../metadata/holder/ReadWorkbookHolder.java | 10 +- .../alibaba/excel/support/ExcelTypeEnum.java | 67 ++++---- .../executor/ExcelWriteFillExecutor.java | 149 +++++++++++------- .../write/metadata/fill/AnalysisCell.java | 19 ++- .../write/metadata/fill/FillWrapper.java | 44 ++++++ .../test/core/dataformat/DateFormatTest.java | 10 +- .../test/core/fill/FillDataTest.java | 44 ++++++ .../test/core/head/ListHeadDataListener.java | 8 +- .../easyexcel/test/demo/fill/FillTest.java | 33 ++++ .../easyexcel/test/temp/FillTempTest.java | 22 +-- .../easyexcel/test/temp/TempFillData.java | 15 ++ .../easyexcel/test/temp/read/CommentTest.java | 4 +- src/test/resources/demo/fill/composite.xlsx | Bin 0 -> 10461 bytes src/test/resources/fill/composite.xls | Bin 0 -> 19968 bytes src/test/resources/fill/composite.xlsx | Bin 0 -> 10461 bytes update.md | 2 +- 23 files changed, 350 insertions(+), 128 deletions(-) create mode 100644 src/main/java/com/alibaba/excel/write/metadata/fill/FillWrapper.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/temp/TempFillData.java create mode 100644 src/test/resources/demo/fill/composite.xlsx create mode 100644 src/test/resources/fill/composite.xls create mode 100644 src/test/resources/fill/composite.xlsx diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java index 4546c4b..5487ee1 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java @@ -59,8 +59,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { } private void choiceExcelExecutor(ReadWorkbook readWorkbook) throws Exception { - ExcelTypeEnum excelType = - ExcelTypeEnum.valueOf(readWorkbook.getFile(), readWorkbook.getInputStream(), readWorkbook.getExcelType()); + ExcelTypeEnum excelType = ExcelTypeEnum.valueOf(readWorkbook); switch (excelType) { case XLS: POIFSFileSystem poifsFileSystem; diff --git a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java index a94a7d3..2b92253 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java @@ -56,6 +56,7 @@ import com.alibaba.excel.analysis.v03.handlers.StringRecordHandler; import com.alibaba.excel.analysis.v03.handlers.TextObjectRecordHandler; import com.alibaba.excel.context.xls.XlsReadContext; import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.exception.ExcelAnalysisStopException; import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; @@ -108,9 +109,14 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor { @Override public List sheetList() { - if (xlsReadContext.readWorkbookHolder().getActualSheetDataList() == null) { - LOGGER.warn("Getting the 'sheetList' before reading will cause the file to be read twice."); - new XlsListSheetListener(xlsReadContext).execute(); + try { + if (xlsReadContext.readWorkbookHolder().getActualSheetDataList() == null) { + new XlsListSheetListener(xlsReadContext).execute(); + } + } catch (ExcelAnalysisStopException e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Custom stop!"); + } } return xlsReadContext.readWorkbookHolder().getActualSheetDataList(); } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java index 973d83e..66e3ab7 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java @@ -23,10 +23,14 @@ public class BofRecordHandler extends AbstractXlsRecordHandler { @Override public void processRecord(XlsReadContext xlsReadContext, Record record) { BOFRecord br = (BOFRecord)record; + XlsReadWorkbookHolder xlsReadWorkbookHolder = xlsReadContext.xlsReadWorkbookHolder(); + if (br.getType() == BOFRecord.TYPE_WORKBOOK) { + xlsReadWorkbookHolder.setReadSheetIndex(null); + return; + } if (br.getType() != BOFRecord.TYPE_WORKSHEET) { return; } - XlsReadWorkbookHolder xlsReadWorkbookHolder = xlsReadContext.xlsReadWorkbookHolder(); // Init read sheet Data initReadSheetDataList(xlsReadWorkbookHolder); Integer readSheetIndex = xlsReadWorkbookHolder.getReadSheetIndex(); diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/DummyRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/DummyRecordHandler.java index ab41d29..54a2d1d 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/DummyRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/DummyRecordHandler.java @@ -8,6 +8,7 @@ import org.apache.poi.hssf.record.Record; import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.RowTypeEnum; import com.alibaba.excel.metadata.Cell; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.read.metadata.holder.ReadRowHolder; @@ -30,6 +31,7 @@ public class DummyRecordHandler extends AbstractXlsRecordHandler implements Igno xlsReadContext.readSheetHolder().getGlobalConfiguration(), xlsReadSheetHolder.getCellMap())); xlsReadContext.analysisEventProcessor().endRow(xlsReadContext); xlsReadSheetHolder.setCellMap(new LinkedHashMap()); + xlsReadSheetHolder.setTempRowType(RowTypeEnum.EMPTY); } else if (record instanceof MissingCellDummyRecord) { MissingCellDummyRecord mcdr = (MissingCellDummyRecord)record; xlsReadSheetHolder.getCellMap().put(mcdr.getColumn(), diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java index 004d9c6..099b308 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java @@ -11,6 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.constant.BuiltinFormats; import com.alibaba.excel.context.xls.XlsReadContext; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.Cell; @@ -52,6 +53,12 @@ public class FormulaRecordHandler extends AbstractXlsRecordHandler implements Ig case NUMERIC: tempCellData.setType(CellDataTypeEnum.NUMBER); tempCellData.setNumberValue(BigDecimal.valueOf(frec.getValue())); + Integer dataFormat = + xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatIndex(frec); + tempCellData.setDataFormat(dataFormat); + tempCellData.setDataFormatString(BuiltinFormats.getBuiltinFormat(dataFormat, + xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatString(frec), + xlsReadContext.readSheetHolder().getGlobalConfiguration().getLocale())); cellMap.put((int)frec.getColumn(), tempCellData); break; case ERROR: diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/RowTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/RowTagHandler.java index ee33266..56fa7ae 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/handlers/RowTagHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/RowTagHandler.java @@ -20,14 +20,25 @@ public class RowTagHandler extends AbstractXlsxTagHandler { @Override public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { - xlsxReadContext.readRowHolder( - new ReadRowHolder(PositionUtils.getRowByRowTagt(attributes.getValue(ExcelXmlConstants.ATTRIBUTE_R)), - RowTypeEnum.DATA, xlsxReadContext.readSheetHolder().getGlobalConfiguration(), null)); + int rowIndex = PositionUtils.getRowByRowTagt(attributes.getValue(ExcelXmlConstants.ATTRIBUTE_R)); + Integer lastIndex = xlsxReadContext.readSheetHolder().getRowIndex(); + if (lastIndex != null) { + while (lastIndex + 1 < rowIndex) { + xlsxReadContext.readRowHolder(new ReadRowHolder(lastIndex + 1, RowTypeEnum.EMPTY, + xlsxReadContext.readSheetHolder().getGlobalConfiguration(), new LinkedHashMap())); + xlsxReadContext.analysisEventProcessor().endRow(xlsxReadContext); + xlsxReadContext.xlsxReadSheetHolder().setCellMap(new LinkedHashMap()); + lastIndex++; + } + } + xlsxReadContext.readSheetHolder().setRowIndex(rowIndex); } @Override public void endElement(XlsxReadContext xlsxReadContext, String name) { - xlsxReadContext.readRowHolder().setCellMap(xlsxReadContext.xlsxReadSheetHolder().getCellMap()); + xlsxReadContext.readRowHolder(new ReadRowHolder(xlsxReadContext.readSheetHolder().getRowIndex(), + RowTypeEnum.DATA, xlsxReadContext.readSheetHolder().getGlobalConfiguration(), + xlsxReadContext.xlsxReadSheetHolder().getCellMap())); xlsxReadContext.analysisEventProcessor().endRow(xlsxReadContext); xlsxReadContext.xlsxReadSheetHolder().setCellMap(new LinkedHashMap()); } diff --git a/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java b/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java index 342d943..81f3586 100644 --- a/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java +++ b/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java @@ -205,9 +205,9 @@ public class ExcelReaderBuilder extends AbstractExcelReaderParameterBuilder List doReadAllSync() { - ExcelReader excelReader = build(); SyncReadListener syncReadListener = new SyncReadListener(); registerReadListener(syncReadListener); + ExcelReader excelReader = build(); excelReader.readAll(); excelReader.finish(); return (List)syncReadListener.getList(); diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java index 7ad8972..375e32c 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java @@ -1,6 +1,5 @@ package com.alibaba.excel.read.metadata.holder; -import java.io.BufferedInputStream; import java.io.File; import java.io.InputStream; import java.util.HashSet; @@ -126,16 +125,9 @@ public class ReadWorkbookHolder extends AbstractReadHolder { super(readWorkbook, null, readWorkbook.getConvertAllFiled()); this.readWorkbook = readWorkbook; if (readWorkbook.getInputStream() != null) { - if (readWorkbook.getInputStream().markSupported()) { - this.inputStream = readWorkbook.getInputStream(); - } else { - this.inputStream = new BufferedInputStream(readWorkbook.getInputStream()); - } + this.inputStream = readWorkbook.getInputStream(); } this.file = readWorkbook.getFile(); - if (file == null && inputStream == null) { - throw new ExcelAnalysisException("File and inputStream must be a non-null."); - } if (readWorkbook.getMandatoryUseInputStream() == null) { this.mandatoryUseInputStream = Boolean.FALSE; } else { diff --git a/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java b/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java index b4e41df..100775d 100644 --- a/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java +++ b/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java @@ -3,12 +3,13 @@ package com.alibaba.excel.support; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; -import java.io.IOException; import java.io.InputStream; import org.apache.poi.poifs.filesystem.FileMagic; +import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.exception.ExcelCommonException; +import com.alibaba.excel.read.metadata.ReadWorkbook; /** * @author jipengfei @@ -29,44 +30,56 @@ public enum ExcelTypeEnum { this.setValue(value); } - public static ExcelTypeEnum valueOf(File file, InputStream inputStream, ExcelTypeEnum excelType) { + public static ExcelTypeEnum valueOf(ReadWorkbook readWorkbook) { + ExcelTypeEnum excelType = readWorkbook.getExcelType(); + if (excelType != null) { + return excelType; + } + File file = readWorkbook.getFile(); + InputStream inputStream = readWorkbook.getInputStream(); + if (file == null && inputStream == null) { + throw new ExcelAnalysisException("File and inputStream must be a non-null."); + } try { - FileMagic fileMagic; if (file != null) { + if (!file.exists()) { + throw new ExcelAnalysisException("File " + file.getAbsolutePath() + " not exists."); + } + String fileName = file.getName(); + if (fileName.endsWith(XLSX.getValue())) { + return XLSX; + } else if (fileName.endsWith(XLS.getValue())) { + return XLS; + } BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file)); try { - fileMagic = FileMagic.valueOf(bufferedInputStream); + return recognitionExcelType(bufferedInputStream); } finally { bufferedInputStream.close(); } - if (!FileMagic.OLE2.equals(fileMagic) && !FileMagic.OOXML.equals(fileMagic)) { - String fileName = file.getName(); - if (fileName.endsWith(XLSX.getValue())) { - return XLSX; - } else if (fileName.endsWith(XLS.getValue())) { - return XLS; - } else { - throw new ExcelCommonException("Unknown excel type."); - } - } - } else { - fileMagic = FileMagic.valueOf(inputStream); } - if (FileMagic.OLE2.equals(fileMagic)) { - return XLS; - } - if (FileMagic.OOXML.equals(fileMagic)) { - return XLSX; - } - } catch (IOException e) { - if (excelType != null) { - return excelType; + if (!inputStream.markSupported()) { + inputStream = new BufferedInputStream(inputStream); + readWorkbook.setInputStream(inputStream); } + return recognitionExcelType(inputStream); + } catch (ExcelCommonException e) { + throw e; + } catch (ExcelAnalysisException e) { + throw e; + } catch (Exception e) { throw new ExcelCommonException( "Convert excel format exception.You can try specifying the 'excelType' yourself", e); } - if (excelType != null) { - return excelType; + } + + private static ExcelTypeEnum recognitionExcelType(InputStream inputStream) throws Exception { + FileMagic fileMagic = FileMagic.valueOf(inputStream); + if (FileMagic.OLE2.equals(fileMagic)) { + return XLS; + } + if (FileMagic.OOXML.equals(fileMagic)) { + return XLSX; } throw new ExcelCommonException( "Convert excel format exception.You can try specifying the 'excelType' yourself"); diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java index 75aa660..0b8889a 100644 --- a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java @@ -27,6 +27,7 @@ import com.alibaba.excel.util.StringUtils; import com.alibaba.excel.util.WriteHandlerUtils; import com.alibaba.excel.write.metadata.fill.AnalysisCell; import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import net.sf.cglib.beans.BeanMap; @@ -47,28 +48,36 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { /** * Fields to replace in the template */ - private Map> templateAnalysisCache = new HashMap>(8); + private Map> templateAnalysisCache = new HashMap>(8); /** * Collection fields to replace in the template */ - private Map> templateCollectionAnalysisCache = - new HashMap>(8); + private Map> templateCollectionAnalysisCache = + new HashMap>(8); /** * Style cache for collection fields */ - private Map> collectionFieldStyleCache = - new HashMap>(8); + private Map> collectionFieldStyleCache = + new HashMap>(8); /** * Row height cache for collection */ - private Map collectionRowHeightCache = new HashMap(8); + private Map collectionRowHeightCache = new HashMap(8); /** * Last index cache for collection fields */ - private Map> collectionLastIndexCache = - new HashMap>(8); + private Map> collectionLastIndexCache = + new HashMap>(8); - private Map relativeRowIndexMap = new HashMap(8); + private Map relativeRowIndexMap = new HashMap(8); + /** + * The data prefix that is populated this time + */ + private String currentDataPrefix; + /** + * The unique data encoding for this fill + */ + private String currentUniqueDataFlag; public ExcelWriteFillExecutor(WriteContext writeContext) { super(writeContext); @@ -79,9 +88,22 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { fillConfig = FillConfig.builder().build(true); } fillConfig.init(); - if (data instanceof Collection) { + + Object realData; + if (data instanceof FillWrapper) { + FillWrapper fillWrapper = (FillWrapper)data; + currentDataPrefix = fillWrapper.getName(); + realData = fillWrapper.getCollectionData(); + } else { + realData = data; + currentDataPrefix = null; + } + currentUniqueDataFlag = uniqueDataFlag(writeContext.writeSheetHolder().getSheetNo(), currentDataPrefix); + + // processing data + if (realData instanceof Collection) { List analysisCellList = readTemplateData(templateCollectionAnalysisCache); - Collection collectionData = (Collection)data; + Collection collectionData = (Collection)realData; if (CollectionUtils.isEmpty(collectionData)) { return; } @@ -93,7 +115,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex()); } } else { - doFill(readTemplateData(templateAnalysisCache), data, fillConfig, null); + doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null); } } @@ -102,8 +124,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { return; } int maxRowIndex = 0; - Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); - Map collectionLastIndexMap = collectionLastIndexCache.get(sheetNo); + Map collectionLastIndexMap = collectionLastIndexCache.get(currentUniqueDataFlag); for (AnalysisCell analysisCell : analysisCellList) { if (collectionLastIndexMap != null) { Integer lastRowIndex = collectionLastIndexMap.get(analysisCell); @@ -132,7 +153,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { return; } sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number, true, false); - for (AnalysisCell analysisCell : templateAnalysisCache.get(writeContext.writeSheetHolder().getSheetNo())) { + for (AnalysisCell analysisCell : templateAnalysisCache.get(currentUniqueDataFlag)) { if (analysisCell.getRowIndex() > maxRowIndex) { analysisCell.setRowIndex(analysisCell.getRowIndex() + number); } @@ -206,14 +227,13 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } private Integer getRelativeRowIndex() { - Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); - Integer relativeRowIndex = relativeRowIndexMap.get(sheetNo); + Integer relativeRowIndex = relativeRowIndexMap.get(currentUniqueDataFlag); if (relativeRowIndex == null) { relativeRowIndex = 0; } else { relativeRowIndex++; } - relativeRowIndexMap.put(sheetNo, relativeRowIndex); + relativeRowIndexMap.put(currentUniqueDataFlag, relativeRowIndex); return relativeRowIndex; } @@ -222,13 +242,12 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { return cachedSheet.getRow(analysisCell.getRowIndex()).getCell(analysisCell.getColumnIndex()); } - Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); Sheet sheet = writeContext.writeSheetHolder().getSheet(); - Map collectionLastIndexMap = collectionLastIndexCache.get(sheetNo); + Map collectionLastIndexMap = collectionLastIndexCache.get(currentUniqueDataFlag); if (collectionLastIndexMap == null) { collectionLastIndexMap = new HashMap(16); - collectionLastIndexCache.put(sheetNo, collectionLastIndexMap); + collectionLastIndexCache.put(currentUniqueDataFlag, collectionLastIndexMap); } boolean isOriginalCell = false; Integer lastRowIndex; @@ -269,10 +288,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } else { row = sheet.createRow(lastRowIndex); } - checkRowHeight(analysisCell, fillConfig, isOriginalCell, row, sheetNo); + checkRowHeight(analysisCell, fillConfig, isOriginalCell, row); WriteHandlerUtils.afterRowCreate(writeContext, row, null, Boolean.FALSE); } else { - checkRowHeight(analysisCell, fillConfig, isOriginalCell, row, sheetNo); + checkRowHeight(analysisCell, fillConfig, isOriginalCell, row); } } Cell cell = row.getCell(lastColumnIndex); @@ -282,10 +301,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { WriteHandlerUtils.afterCellCreate(writeContext, cell, null, null, Boolean.FALSE); } - Map collectionFieldStyleMap = collectionFieldStyleCache.get(sheetNo); + Map collectionFieldStyleMap = collectionFieldStyleCache.get(currentUniqueDataFlag); if (collectionFieldStyleMap == null) { collectionFieldStyleMap = new HashMap(16); - collectionFieldStyleCache.put(sheetNo, collectionFieldStyleMap); + collectionFieldStyleCache.put(currentUniqueDataFlag, collectionFieldStyleMap); } if (isOriginalCell) { collectionFieldStyleMap.put(analysisCell, cell.getCellStyle()); @@ -298,31 +317,27 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { return cell; } - private void checkRowHeight(AnalysisCell analysisCell, FillConfig fillConfig, boolean isOriginalCell, Row row, - Integer sheetNo) { - if (!analysisCell.getFirstColumn() || !WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection())) { + private void checkRowHeight(AnalysisCell analysisCell, FillConfig fillConfig, boolean isOriginalCell, Row row) { + if (!analysisCell.getFirstRow() || !WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection())) { return; } if (isOriginalCell) { - collectionRowHeightCache.put(sheetNo, row.getHeight()); + collectionRowHeightCache.put(currentUniqueDataFlag, row.getHeight()); return; } - Short rowHeight = collectionRowHeightCache.get(sheetNo); + Short rowHeight = collectionRowHeightCache.get(currentUniqueDataFlag); if (rowHeight != null) { row.setHeight(rowHeight); } } - private List readTemplateData(Map> analysisCache) { - Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); - List analysisCellList = analysisCache.get(sheetNo); + private List readTemplateData(Map> analysisCache) { + List analysisCellList = analysisCache.get(currentUniqueDataFlag); if (analysisCellList != null) { return analysisCellList; } Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); - analysisCellList = new ArrayList(); - List collectionAnalysisCellList = new ArrayList(); - Set firstColumnCache = new HashSet(); + Map> firstRowCache = new HashMap>(8); for (int i = 0; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); if (row == null) { @@ -333,32 +348,26 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { if (cell == null) { continue; } - String preparedData = - prepareData(cell, analysisCellList, collectionAnalysisCellList, i, j, firstColumnCache); + String preparedData = prepareData(cell, i, j, firstRowCache); // Prevent empty data from not being replaced if (preparedData != null) { cell.setCellValue(preparedData); } } } - templateAnalysisCache.put(sheetNo, analysisCellList); - templateCollectionAnalysisCache.put(sheetNo, collectionAnalysisCellList); - return analysisCache.get(sheetNo); + return analysisCache.get(currentUniqueDataFlag); } /** * To prepare data * * @param cell - * @param analysisCellList - * @param collectionAnalysisCellList * @param rowIndex * @param columnIndex - * @param firstColumnCache + * @param firstRowCache * @return Returns the data that the cell needs to replace */ - private String prepareData(Cell cell, List analysisCellList, - List collectionAnalysisCellList, int rowIndex, int columnIndex, Set firstColumnCache) { + private String prepareData(Cell cell, int rowIndex, int columnIndex, Map> firstRowCache) { if (!CellType.STRING.equals(cell.getCellTypeEnum())) { return null; } @@ -404,8 +413,12 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { if (StringUtils.isEmpty(variable)) { continue; } - if (variable.startsWith(COLLECTION_PREFIX)) { - variable = variable.substring(1); + int collectPrefixIndex = variable.indexOf(COLLECTION_PREFIX); + if (collectPrefixIndex > -1) { + if (collectPrefixIndex != 0) { + analysisCell.setPrefix(variable.substring(0, collectPrefixIndex)); + } + variable = variable.substring(collectPrefixIndex + 1); if (StringUtils.isEmpty(variable)) { continue; } @@ -422,13 +435,13 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } lastPrepareDataIndex = suffixIndex + 1; } - return dealAnalysisCell(analysisCell, value, rowIndex, lastPrepareDataIndex, length, analysisCellList, - collectionAnalysisCellList, firstColumnCache, preparedData); + return dealAnalysisCell(analysisCell, value, rowIndex, lastPrepareDataIndex, length, firstRowCache, + preparedData); } private String dealAnalysisCell(AnalysisCell analysisCell, String value, int rowIndex, int lastPrepareDataIndex, - int length, List analysisCellList, List collectionAnalysisCellList, - Set firstColumnCache, StringBuilder preparedData) { + int length, Map> firstRowCache, StringBuilder preparedData) { + Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); if (analysisCell != null) { if (lastPrepareDataIndex == length) { analysisCell.getPrepareDataList().add(StringUtils.EMPTY); @@ -436,12 +449,29 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { analysisCell.getPrepareDataList().add(convertPrepareData(value.substring(lastPrepareDataIndex))); analysisCell.setOnlyOneVariable(Boolean.FALSE); } + String uniqueDataFlag = uniqueDataFlag(sheetNo, analysisCell.getPrefix()); if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { + List analysisCellList = templateAnalysisCache.get(uniqueDataFlag); + if (analysisCellList == null) { + analysisCellList = new ArrayList(); + templateAnalysisCache.put(uniqueDataFlag, analysisCellList); + } analysisCellList.add(analysisCell); } else { - if (!firstColumnCache.contains(rowIndex)) { - analysisCell.setFirstColumn(Boolean.TRUE); - firstColumnCache.add(rowIndex); + Set uniqueFirstRowCache = firstRowCache.get(uniqueDataFlag); + if (uniqueFirstRowCache == null) { + uniqueFirstRowCache = new HashSet(); + firstRowCache.put(uniqueDataFlag, uniqueFirstRowCache); + } + if (!uniqueFirstRowCache.contains(rowIndex)) { + analysisCell.setFirstRow(Boolean.TRUE); + uniqueFirstRowCache.add(rowIndex); + } + + List collectionAnalysisCellList = templateCollectionAnalysisCache.get(uniqueDataFlag); + if (collectionAnalysisCellList == null) { + collectionAnalysisCellList = new ArrayList(); + templateCollectionAnalysisCache.put(uniqueDataFlag, collectionAnalysisCellList); } collectionAnalysisCellList.add(analysisCell); } @@ -460,7 +490,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { List prepareDataList = new ArrayList(); analysisCell.setPrepareDataList(prepareDataList); analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON); - analysisCell.setFirstColumn(Boolean.FALSE); + analysisCell.setFirstRow(Boolean.FALSE); return analysisCell; } @@ -470,4 +500,11 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { return prepareData; } + private String uniqueDataFlag(Integer sheetNo, String wrapperName) { + if (StringUtils.isEmpty(wrapperName)) { + return sheetNo.toString() + "-"; + } + return sheetNo + "-" + wrapperName; + } + } diff --git a/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java index 47a2594..41b2cfc 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java +++ b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java @@ -16,7 +16,8 @@ public class AnalysisCell { private List prepareDataList; private Boolean onlyOneVariable; private WriteTemplateAnalysisCellTypeEnum cellType; - private Boolean firstColumn; + private String prefix; + private Boolean firstRow; public int getColumnIndex() { return columnIndex; @@ -58,6 +59,14 @@ public class AnalysisCell { this.onlyOneVariable = onlyOneVariable; } + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + public WriteTemplateAnalysisCellTypeEnum getCellType() { return cellType; } @@ -66,12 +75,12 @@ public class AnalysisCell { this.cellType = cellType; } - public Boolean getFirstColumn() { - return firstColumn; + public Boolean getFirstRow() { + return firstRow; } - public void setFirstColumn(Boolean firstColumn) { - this.firstColumn = firstColumn; + public void setFirstRow(Boolean firstRow) { + this.firstRow = firstRow; } @Override diff --git a/src/main/java/com/alibaba/excel/write/metadata/fill/FillWrapper.java b/src/main/java/com/alibaba/excel/write/metadata/fill/FillWrapper.java new file mode 100644 index 0000000..e25e4da --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/metadata/fill/FillWrapper.java @@ -0,0 +1,44 @@ +package com.alibaba.excel.write.metadata.fill; + +import java.util.Collection; + +/** + * Multiple lists are supported when packing + * + * @author Jiaju Zhuang + **/ +public class FillWrapper { + /** + * The collection prefix that needs to be filled. + */ + private String name; + /** + * Data that needs to be filled. + */ + private Collection collectionData; + + public FillWrapper(Collection collectionData) { + this.collectionData = collectionData; + } + + public FillWrapper(String name, Collection collectionData) { + this.name = name; + this.collectionData = collectionData; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Collection getCollectionData() { + return collectionData; + } + + public void setCollectionData(Collection collectionData) { + this.collectionData = collectionData; + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java b/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java index 477652b..1ddb07f 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java @@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.fastjson.JSON; /** * @@ -44,9 +45,10 @@ public class DateFormatTest { private void readCn(File file) { List list = EasyExcel.read(file, DateFormatData.class, null).locale(Locale.CHINA).sheet().doReadSync(); + System.out.println(JSON.toJSONString(list)); for (DateFormatData data : list) { - Assert.assertEquals(data.getDate(), data.getDateStringCn()); - Assert.assertEquals(data.getNumber(), data.getNumberStringCn()); + Assert.assertEquals(data.getDateStringCn(), data.getDate()); + Assert.assertEquals(data.getNumberStringCn(), data.getNumber()); } } @@ -54,8 +56,8 @@ public class DateFormatTest { List list = EasyExcel.read(file, DateFormatData.class, null).locale(Locale.US).sheet().doReadSync(); for (DateFormatData data : list) { - Assert.assertEquals(data.getDate(), data.getDateStringUs()); - Assert.assertEquals(data.getNumber(), data.getNumberStringUs()); + Assert.assertEquals(data.getDateStringUs(), data.getDate()); + Assert.assertEquals(data.getNumberStringUs(), data.getNumber()); } } } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java index 04a8bd1..bdcd34c 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java @@ -19,6 +19,7 @@ import com.alibaba.excel.enums.WriteDirectionEnum; import com.alibaba.excel.write.merge.LoopMergeStrategy; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; /** * @@ -39,6 +40,10 @@ public class FillDataTest { private static File horizontalFillTemplate07; private static File fileHorizontal03; private static File horizontalFillTemplate03; + private static File fileComposite07; + private static File compositeFillTemplate07; + private static File fileComposite03; + private static File compositeFillTemplate03; @BeforeClass public static void init() { @@ -54,6 +59,10 @@ public class FillDataTest { horizontalFillTemplate07 = TestFileUtil.readFile("fill" + File.separator + "horizontal.xlsx"); fileHorizontal03 = TestFileUtil.createNewFile("fillHorizontal03.xls"); horizontalFillTemplate03 = TestFileUtil.readFile("fill" + File.separator + "horizontal.xls"); + fileComposite07 = TestFileUtil.createNewFile("fileComposite07.xlsx"); + compositeFillTemplate07 = TestFileUtil.readFile("fill" + File.separator + "composite.xlsx"); + fileComposite03 = TestFileUtil.createNewFile("fileComposite03.xls"); + compositeFillTemplate03 = TestFileUtil.readFile("fill" + File.separator + "composite.xls"); } @Test @@ -86,6 +95,41 @@ public class FillDataTest { horizontalFill(fileHorizontal03, horizontalFillTemplate03); } + @Test + public void t07CompositeFill07() { + compositeFill(fileComposite07, compositeFillTemplate07); + } + + @Test + public void t08CompositeFill03() { + compositeFill(fileComposite03, compositeFillTemplate03); + } + + private void compositeFill(File file, File template) { + ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + + List list = EasyExcel.read(file).ignoreEmptyRow(false).sheet().headRowNumber(0).doReadSync(); + Map map0 = (Map)list.get(0); + Assert.assertEquals("张三", map0.get(21)); + Map map27 = (Map)list.get(27); + Assert.assertEquals("张三", map27.get(0)); + Map map29 = (Map)list.get(29); + Assert.assertEquals("张三", map29.get(3)); + } + private void horizontalFill(File file, File template) { ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); diff --git a/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataListener.java b/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataListener.java index 81e85ae..c57976a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataListener.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataListener.java @@ -29,10 +29,10 @@ public class ListHeadDataListener extends AnalysisEventListener data = list.get(0); - Assert.assertEquals(data.get(0), "字符串0"); - Assert.assertEquals(data.get(1), "1.0"); - Assert.assertEquals(data.get(2), "2020-01-01 01:01:01"); - Assert.assertEquals(data.get(3), "额外数据"); + Assert.assertEquals("字符串0", data.get(0)); + Assert.assertEquals("1", data.get(1)); + Assert.assertEquals("2020-01-01 01:01:01", data.get(2)); + Assert.assertEquals("额外数据", data.get(3)); LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); } } diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java index 32a0185..068045e 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java @@ -15,6 +15,7 @@ import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.enums.WriteDirectionEnum; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; /** * 写的填充写法 @@ -179,6 +180,38 @@ public class FillTest { excelWriter.finish(); } + /** + * 组合填充填充 + * + * @since 2.2.0 + */ + @Test + public void compositeFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 {前缀.} 前缀可以区分不同的list + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "composite.xlsx"; + + String fileName = TestFileUtil.getPath() + "compositeFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + // 如果有多个list 模板上必须有{前缀.} 这里的前缀就是 data1,然后多个list必须用 FillWrapper包裹 + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // 别忘记关闭流 + excelWriter.finish(); + } + private List data() { List list = new ArrayList(); for (int i = 0; i < 10; i++) { diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java index 8049d89..0ed625d 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java @@ -14,7 +14,6 @@ import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.write.metadata.WriteSheet; -import com.alibaba.excel.write.metadata.fill.FillConfig; /** * 写的填充写法 @@ -34,18 +33,12 @@ public class FillTempTest { public void complexFill() { // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 // {} 代表普通变量 {.} 代表是list的变量 - String templateFileName = "D:\\test\\complex.xlsx"; + String templateFileName = "D:\\test\\simple.xlsx"; String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx"; ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); - // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。 - // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用 - // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存 - // 如果数据量大 list不是最后一行 参照下一个 - FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); - excelWriter.fill(data(), fillConfig, writeSheet); -// excelWriter.fill(data2(), fillConfig, writeSheet); + excelWriter.fill(teamp(), writeSheet); Map map = new HashMap(); map.put("date", "2019年10月9日13:28:28"); map.put("total", 1000); @@ -106,6 +99,17 @@ public class FillTempTest { return list; } + private List teamp() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + TempFillData fillData = new TempFillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + } + return list; + } + private List data() { List list = new ArrayList(); for (int i = 0; i < 10; i++) { diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/TempFillData.java b/src/test/java/com/alibaba/easyexcel/test/temp/TempFillData.java new file mode 100644 index 0000000..55984a7 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/TempFillData.java @@ -0,0 +1,15 @@ +package com.alibaba.easyexcel.test.temp; + +import com.alibaba.excel.annotation.write.style.ContentRowHeight; + +import lombok.Data; + +/** + * @author Jiaju Zhuang + */ +@Data +@ContentRowHeight(30) +public class TempFillData { + private String name; + private double number; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/read/CommentTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/read/CommentTest.java index 21e0d9c..2c9235a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/read/CommentTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/read/CommentTest.java @@ -24,10 +24,10 @@ public class CommentTest { @Test public void comment() throws Exception { - File file = new File("D:\\test\\comment.xls"); + File file = new File("D:\\test\\listHead07.xlsx"); List> datas = EasyExcel.read(file).doReadAllSync(); for (Map data : datas) { - LOGGER.info("数据:{}", JSON.toJSONString(data.get(0))); + LOGGER.info("数据:{}", JSON.toJSONString(data)); } } diff --git a/src/test/resources/demo/fill/composite.xlsx b/src/test/resources/demo/fill/composite.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c76a2b12ba8576c8394009436ec01ba7e88c89e1 GIT binary patch literal 10461 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

u{y6O^mTHrURC|_V1#A9 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 DQ|0>1 literal 0 HcmV?d00001 diff --git a/src/test/resources/fill/composite.xlsx b/src/test/resources/fill/composite.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c76a2b12ba8576c8394009436ec01ba7e88c89e1 GIT binary patch literal 10461 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和`class`的head,会通过index去匹配注解 * 修复读取转换器的并发问题 -* +* 填充支持多个List对象 # 2.1.4 * 新增参数`useDefaultListener` 可以排除默认对象转换