package com.alibaba.excel.write.executor; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.WriteDirectionEnum; import com.alibaba.excel.enums.WriteTemplateAnalysisCellTypeEnum; import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.util.CollectionUtils; 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; /** * Fill the data into excel * * @author Jiaju Zhuang */ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { private static final String ESCAPE_FILL_PREFIX = "\\\\\\{"; private static final String ESCAPE_FILL_SUFFIX = "\\\\\\}"; private static final String FILL_PREFIX = "{"; private static final String FILL_SUFFIX = "}"; private static final char IGNORE_CHAR = '\\'; private static final String COLLECTION_PREFIX = "."; /** * Fields to replace in the template */ private final Map> templateAnalysisCache = new HashMap>(8); /** * Collection fields to replace in the template */ private final Map> templateCollectionAnalysisCache = new HashMap>(8); /** * Style cache for collection fields */ private final Map> collectionFieldStyleCache = new HashMap>(8); /** * Row height cache for collection */ private final Map collectionRowHeightCache = new HashMap(8); /** * Last index cache for collection fields */ private final Map> collectionLastIndexCache = new HashMap>(8); private final 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); } public void fill(Object data, FillConfig fillConfig) { if (data == null) { data = new HashMap(16); } if (fillConfig == null) { fillConfig = FillConfig.builder().build(true); } fillConfig.init(); 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(), currentDataPrefix); // processing data if (realData instanceof Collection) { List analysisCellList = readTemplateData(templateCollectionAnalysisCache); Collection collectionData = (Collection) realData; if (CollectionUtils.isEmpty(collectionData)) { return; } Iterator iterator = collectionData.iterator(); if (WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) && fillConfig.getForceNewRow()) { shiftRows(collectionData.size(), analysisCellList); } while (iterator.hasNext()) { doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex()); } } else { doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null); } } private void shiftRows(int size, List analysisCellList) { if (CollectionUtils.isEmpty(analysisCellList)) { return; } int maxRowIndex = 0; Map collectionLastIndexMap = collectionLastIndexCache.get(currentUniqueDataFlag); for (AnalysisCell analysisCell : analysisCellList) { if (collectionLastIndexMap != null) { Integer lastRowIndex = collectionLastIndexMap.get(analysisCell); if (lastRowIndex != null) { if (lastRowIndex > maxRowIndex) { maxRowIndex = lastRowIndex; } continue; } } if (analysisCell.getRowIndex() > maxRowIndex) { maxRowIndex = analysisCell.getRowIndex(); } } Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); int lastRowIndex = cachedSheet.getLastRowNum(); if (maxRowIndex >= lastRowIndex) { return; } Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); int number = size; if (collectionLastIndexMap == null) { number--; } if (number <= 0) { return; } sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number, true, false); // The current data is greater than unity rowindex increase String tablePrefix = tablePrefix(currentUniqueDataFlag); increaseRowIndex(templateAnalysisCache, number, maxRowIndex, tablePrefix); increaseRowIndex(templateCollectionAnalysisCache, number, maxRowIndex, tablePrefix); } private void increaseRowIndex(Map> templateAnalysisCache, int number, int maxRowIndex, String tablePrefix) { for (Map.Entry> entry : templateAnalysisCache.entrySet()) { if (!tablePrefix.equals(tablePrefix(entry.getKey()))) { continue; } for (AnalysisCell analysisCell : entry.getValue()) { if (analysisCell.getRowIndex() > maxRowIndex) { analysisCell.setRowIndex(analysisCell.getRowIndex() + number); } } } } private void doFill(List analysisCellList, Object oneRowData, FillConfig fillConfig, Integer relativeRowIndex) { Map dataMap; if (oneRowData instanceof Map) { dataMap = (Map) oneRowData; } else { dataMap = BeanMap.create(oneRowData); } WriteSheetHolder writeSheetHolder = writeContext.writeSheetHolder(); Map fieldNameContentPropertyMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getFieldNameContentPropertyMap(); for (AnalysisCell analysisCell : analysisCellList) { Cell cell = getOneCell(analysisCell, fillConfig); if (analysisCell.getOnlyOneVariable()) { String variable = analysisCell.getVariableList().get(0); if (!dataMap.containsKey(variable)) { continue; } Object value = dataMap.get(variable); CellData cellData = converterAndSet(writeSheetHolder, value == null ? null : value.getClass(), cell, value, fieldNameContentPropertyMap.get(variable), null, relativeRowIndex); WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE); } else { StringBuilder cellValueBuild = new StringBuilder(); int index = 0; List cellDataList = new ArrayList(); for (String variable : analysisCell.getVariableList()) { cellValueBuild.append(analysisCell.getPrepareDataList().get(index++)); if (!dataMap.containsKey(variable)) { continue; } Object value = dataMap.get(variable); CellData cellData = convert(writeSheetHolder, value == null ? null : value.getClass(), cell, value, fieldNameContentPropertyMap.get(variable)); cellDataList.add(cellData); CellDataTypeEnum type = cellData.getType(); if (type != null) { switch (type) { case STRING: cellValueBuild.append(cellData.getStringValue()); break; case BOOLEAN: cellValueBuild.append(cellData.getBooleanValue()); break; case NUMBER: cellValueBuild.append(cellData.getNumberValue()); break; default: break; } } } cellValueBuild.append(analysisCell.getPrepareDataList().get(index)); cell.setCellValue(cellValueBuild.toString()); WriteHandlerUtils.afterCellDispose(writeContext, cellDataList, cell, null, relativeRowIndex, Boolean.FALSE); } } } private Integer getRelativeRowIndex() { Integer relativeRowIndex = relativeRowIndexMap.get(currentUniqueDataFlag); if (relativeRowIndex == null) { relativeRowIndex = 0; } else { relativeRowIndex++; } relativeRowIndexMap.put(currentUniqueDataFlag, relativeRowIndex); return relativeRowIndex; } private Cell getOneCell(AnalysisCell analysisCell, FillConfig fillConfig) { Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { return cachedSheet.getRow(analysisCell.getRowIndex()).getCell(analysisCell.getColumnIndex()); } Sheet sheet = writeContext.writeSheetHolder().getSheet(); Map collectionLastIndexMap = collectionLastIndexCache.get(currentUniqueDataFlag); if (collectionLastIndexMap == null) { collectionLastIndexMap = new HashMap(16); collectionLastIndexCache.put(currentUniqueDataFlag, collectionLastIndexMap); } boolean isOriginalCell = false; Integer lastRowIndex; Integer lastColumnIndex; switch (fillConfig.getDirection()) { case VERTICAL: lastRowIndex = collectionLastIndexMap.get(analysisCell); if (lastRowIndex == null) { lastRowIndex = analysisCell.getRowIndex(); collectionLastIndexMap.put(analysisCell, lastRowIndex); isOriginalCell = true; } else { collectionLastIndexMap.put(analysisCell, ++lastRowIndex); } lastColumnIndex = analysisCell.getColumnIndex(); break; case HORIZONTAL: lastRowIndex = analysisCell.getRowIndex(); lastColumnIndex = collectionLastIndexMap.get(analysisCell); if (lastColumnIndex == null) { lastColumnIndex = analysisCell.getColumnIndex(); collectionLastIndexMap.put(analysisCell, lastColumnIndex); isOriginalCell = true; } else { collectionLastIndexMap.put(analysisCell, ++lastColumnIndex); } break; default: throw new ExcelGenerateException("The wrong direction."); } Row row = createRowIfNecessary(sheet, cachedSheet, lastRowIndex, fillConfig, analysisCell, isOriginalCell); Cell cell = createCellIfNecessary(row,lastColumnIndex); Map collectionFieldStyleMap = collectionFieldStyleCache.get(currentUniqueDataFlag); if (collectionFieldStyleMap == null) { collectionFieldStyleMap = new HashMap(16); collectionFieldStyleCache.put(currentUniqueDataFlag, collectionFieldStyleMap); } if (isOriginalCell) { collectionFieldStyleMap.put(analysisCell, cell.getCellStyle()); } else { CellStyle cellStyle = collectionFieldStyleMap.get(analysisCell); if (cellStyle != null) { cell.setCellStyle(cellStyle); } } return cell; } private Cell createCellIfNecessary(Row row, Integer lastColumnIndex) { Cell cell = row.getCell(lastColumnIndex); if (cell != null) { return cell; } WriteHandlerUtils.beforeCellCreate(writeContext, row, null, lastColumnIndex, null, Boolean.FALSE); cell = row.createCell(lastColumnIndex); WriteHandlerUtils.afterCellCreate(writeContext, cell, null, null, Boolean.FALSE); return cell; } private Row createRowIfNecessary(Sheet sheet, Sheet cachedSheet, Integer lastRowIndex, FillConfig fillConfig, AnalysisCell analysisCell, boolean isOriginalCell) { Row row = sheet.getRow(lastRowIndex); if (row != null) { return row; } row = cachedSheet.getRow(lastRowIndex); if (row == null) { WriteHandlerUtils.beforeRowCreate(writeContext, lastRowIndex, null, Boolean.FALSE); if (fillConfig.getForceNewRow()) { row = cachedSheet.createRow(lastRowIndex); } else { // The last row of the middle disk inside empty rows, resulting in cachedSheet can not get inside. // Will throw Attempting to write a row[" + rownum + "] " + "in the range [0," + this._sh.getLastRowNum() + "] that is already written to disk. try { row = sheet.createRow(lastRowIndex); } catch (IllegalArgumentException ignore) { row = cachedSheet.createRow(lastRowIndex); } } checkRowHeight(analysisCell, fillConfig, isOriginalCell, row); WriteHandlerUtils.afterRowCreate(writeContext, row, null, Boolean.FALSE); } else { checkRowHeight(analysisCell, fillConfig, isOriginalCell, row); } return row; } private void checkRowHeight(AnalysisCell analysisCell, FillConfig fillConfig, boolean isOriginalCell, Row row) { if (!analysisCell.getFirstRow() || !WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection())) { return; } if (isOriginalCell) { collectionRowHeightCache.put(currentUniqueDataFlag, row.getHeight()); return; } Short rowHeight = collectionRowHeightCache.get(currentUniqueDataFlag); if (rowHeight != null) { row.setHeight(rowHeight); } } private List readTemplateData(Map> analysisCache) { List analysisCellList = analysisCache.get(currentUniqueDataFlag); if (analysisCellList != null) { return analysisCellList; } Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); Map> firstRowCache = new HashMap>(8); for (int i = 0; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); if (row == null) { continue; } for (int j = 0; j < row.getLastCellNum(); j++) { Cell cell = row.getCell(j); if (cell == null) { continue; } String preparedData = prepareData(cell, i, j, firstRowCache); // Prevent empty data from not being replaced if (preparedData != null) { cell.setCellValue(preparedData); } } } return analysisCache.get(currentUniqueDataFlag); } /** * To prepare data * * @param cell * @param rowIndex * @param columnIndex * @param firstRowCache * @return Returns the data that the cell needs to replace */ private String prepareData(Cell cell, int rowIndex, int columnIndex, Map> firstRowCache) { if (!CellType.STRING.equals(cell.getCellTypeEnum())) { return null; } String value = cell.getStringCellValue(); if (StringUtils.isEmpty(value)) { return null; } StringBuilder preparedData = new StringBuilder(); AnalysisCell analysisCell = null; int startIndex = 0; int length = value.length(); int lastPrepareDataIndex = 0; out: while (startIndex < length) { int prefixIndex = value.indexOf(FILL_PREFIX, startIndex); if (prefixIndex < 0) { break out; } if (prefixIndex != 0) { char prefixPrefixChar = value.charAt(prefixIndex - 1); if (prefixPrefixChar == IGNORE_CHAR) { startIndex = prefixIndex + 1; continue; } } int suffixIndex = -1; while (suffixIndex == -1 && startIndex < length) { suffixIndex = value.indexOf(FILL_SUFFIX, startIndex + 1); if (suffixIndex < 0) { break out; } startIndex = suffixIndex + 1; char prefixSuffixChar = value.charAt(suffixIndex - 1); if (prefixSuffixChar == IGNORE_CHAR) { suffixIndex = -1; } } if (analysisCell == null) { analysisCell = initAnalysisCell(rowIndex, columnIndex); } String variable = value.substring(prefixIndex + 1, suffixIndex); if (StringUtils.isEmpty(variable)) { continue; } 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; } analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COLLECTION); } analysisCell.getVariableList().add(variable); if (lastPrepareDataIndex == prefixIndex) { analysisCell.getPrepareDataList().add(StringUtils.EMPTY); } else { String data = convertPrepareData(value.substring(lastPrepareDataIndex, prefixIndex)); preparedData.append(data); analysisCell.getPrepareDataList().add(data); analysisCell.setOnlyOneVariable(Boolean.FALSE); } lastPrepareDataIndex = suffixIndex + 1; } return dealAnalysisCell(analysisCell, value, rowIndex, lastPrepareDataIndex, length, firstRowCache, preparedData); } private String dealAnalysisCell(AnalysisCell analysisCell, String value, int rowIndex, int lastPrepareDataIndex, int length, Map> firstRowCache, StringBuilder preparedData) { if (analysisCell != null) { if (lastPrepareDataIndex == length) { analysisCell.getPrepareDataList().add(StringUtils.EMPTY); } else { analysisCell.getPrepareDataList().add(convertPrepareData(value.substring(lastPrepareDataIndex))); analysisCell.setOnlyOneVariable(Boolean.FALSE); } String uniqueDataFlag = uniqueDataFlag(writeContext.writeSheetHolder(), 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 { 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); } return preparedData.toString(); } return null; } private AnalysisCell initAnalysisCell(Integer rowIndex, Integer columnIndex) { AnalysisCell analysisCell = new AnalysisCell(); analysisCell.setRowIndex(rowIndex); analysisCell.setColumnIndex(columnIndex); analysisCell.setOnlyOneVariable(Boolean.TRUE); List variableList = new ArrayList(); analysisCell.setVariableList(variableList); List prepareDataList = new ArrayList(); analysisCell.setPrepareDataList(prepareDataList); analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON); analysisCell.setFirstRow(Boolean.FALSE); return analysisCell; } private String convertPrepareData(String prepareData) { prepareData = prepareData.replaceAll(ESCAPE_FILL_PREFIX, FILL_PREFIX); prepareData = prepareData.replaceAll(ESCAPE_FILL_SUFFIX, FILL_SUFFIX); return prepareData; } private String uniqueDataFlag(WriteSheetHolder writeSheetHolder, String wrapperName) { String prefix; if (writeSheetHolder.getSheetNo() != null) { prefix = writeSheetHolder.getSheetNo().toString(); } else { prefix = writeSheetHolder.getSheetName().toString(); } if (StringUtils.isEmpty(wrapperName)) { return prefix + "-"; } return prefix + "-" + wrapperName; } private String tablePrefix(String uniqueDataFlag) { return uniqueDataFlag.substring(0, uniqueDataFlag.indexOf("-") + 1); } }