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 0000000..c76a2b1 Binary files /dev/null and b/src/test/resources/demo/fill/composite.xlsx differ diff --git a/src/test/resources/fill/composite.xls b/src/test/resources/fill/composite.xls new file mode 100644 index 0000000..e48aa0c Binary files /dev/null and b/src/test/resources/fill/composite.xls differ diff --git a/src/test/resources/fill/composite.xlsx b/src/test/resources/fill/composite.xlsx new file mode 100644 index 0000000..c76a2b1 Binary files /dev/null and b/src/test/resources/fill/composite.xlsx differ diff --git a/update.md b/update.md index cdff209..0d34e83 100644 --- a/update.md +++ b/update.md @@ -13,7 +13,7 @@ * 兼容部分比较特殊的excel * 同时传入了`List>`和`class`的head,会通过index去匹配注解 * 修复读取转换器的并发问题 -* +* 填充支持多个List对象 # 2.1.4 * 新增参数`useDefaultListener` 可以排除默认对象转换