diff --git a/fesod-examples/src/test/java/org/apache/fesod/excel/temp/fill/DynamicFillData.java b/fesod-examples/src/test/java/org/apache/fesod/excel/temp/fill/DynamicFillData.java new file mode 100644 index 000000000..4af1206b3 --- /dev/null +++ b/fesod-examples/src/test/java/org/apache/fesod/excel/temp/fill/DynamicFillData.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fesod.excel.temp.fill; + +import java.util.Map; +import lombok.Data; +import org.apache.fesod.excel.annotation.fill.DynamicColumn; + +@Data +public class DynamicFillData { + private String name; + private double number; + + @DynamicColumn() + private Map qtyMap; + + @DynamicColumn() + private Map priceMap; +} diff --git a/fesod-examples/src/test/java/org/apache/fesod/excel/temp/fill/DynamicFillDataObj.java b/fesod-examples/src/test/java/org/apache/fesod/excel/temp/fill/DynamicFillDataObj.java new file mode 100644 index 000000000..c033dccee --- /dev/null +++ b/fesod-examples/src/test/java/org/apache/fesod/excel/temp/fill/DynamicFillDataObj.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fesod.excel.temp.fill; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@AllArgsConstructor +@Getter +@Setter +public class DynamicFillDataObj { + + private String qty; + private double price; +} diff --git a/fesod-examples/src/test/java/org/apache/fesod/excel/temp/fill/FillTempTest.java b/fesod-examples/src/test/java/org/apache/fesod/excel/temp/fill/FillTempTest.java index 1366aa814..5c6ad570e 100644 --- a/fesod-examples/src/test/java/org/apache/fesod/excel/temp/fill/FillTempTest.java +++ b/fesod-examples/src/test/java/org/apache/fesod/excel/temp/fill/FillTempTest.java @@ -72,6 +72,83 @@ public void simpleFill() { */ } + @Test + public void dynamicFill() { + String templateFileName = "src/test/resources/fill/dynamicColumn.xlsx"; + String fileName = TestFileUtil.getPath() + "dynamicColumnFill" + System.currentTimeMillis() + ".xlsx"; + + DynamicFillData fillData1 = new DynamicFillData(); + fillData1.setName("Zhang San"); + fillData1.setNumber(5.2); + HashMap qtyMap = new HashMap<>(); + qtyMap.put("2023-01-01", "100"); + qtyMap.put("2023-01-02", "200"); + qtyMap.put("2023-01-03", "300"); + fillData1.setQtyMap(qtyMap); + HashMap priceMap = new HashMap<>(); + priceMap.put("2023-01-01", new DynamicFillDataObj("100个", 100)); + priceMap.put("2023-01-02", new DynamicFillDataObj("200个", 200)); + priceMap.put("2023-01-03", new DynamicFillDataObj("300个", 300)); + fillData1.setPriceMap(priceMap); + + DynamicFillData fillData2 = new DynamicFillData(); + fillData2.setName("Li Si"); + fillData2.setNumber(6.3); + HashMap qtyMap2 = new HashMap<>(); + qtyMap2.put("2023-01-01", "100"); + qtyMap2.put("2023-01-02", "200"); + qtyMap2.put("2023-01-03", "300"); + fillData2.setQtyMap(qtyMap2); + HashMap priceMap2 = new HashMap<>(); + priceMap2.put("2023-01-01", new DynamicFillDataObj("100", 100)); + priceMap2.put("2023-01-02", new DynamicFillDataObj("200", 200)); + priceMap2.put("2023-01-03", new DynamicFillDataObj("300", 300)); + fillData2.setPriceMap(priceMap2); + + List fillDataList = new ArrayList<>(); + fillDataList.add(fillData1); + fillDataList.add(fillData2); + + ArrayList dateList = new ArrayList<>(); + dateList.add("2023-01-01"); + dateList.add("2023-01-02"); + dateList.add("2023-01-03"); + dateList.add("2023-01-04"); + dateList.add("2023-01-05"); + dateList.add("2023-01-06"); + dateList.add("2023-01-07"); + dateList.add("2023-01-08"); + dateList.add("2023-01-09"); + dateList.add("2023-01-10"); + dateList.add("2023-01-11"); + dateList.add("2023-01-12"); + + ExcelWriter excelWriter = + FastExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = FastExcel.writerSheet().build(); + excelWriter.fill( + new FillWrapper("dataList", fillDataList), + FillConfig.builder() + .forceNewRow(true) + .addDynamicInfo(dateList, 1, "qtyMap") + .addDefaultDynamicInfo(dateList, 1) + .build(), + writeSheet); + excelWriter.fill( + new FillWrapper("dataObjList", fillDataList), + FillConfig.builder() + .addDefaultDynamicInfo(dateList, 2) + .forceNewRow(true) + .build(), + writeSheet); + excelWriter.fill( + new FillWrapper("dateList", dateList), + FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(), + writeSheet); + // Do not forget to close the stream + excelWriter.finish(); + } + /** * Example of filling a list of data. * @@ -205,7 +282,7 @@ public void horizontalFill() { excelWriter.fill(data(), fillConfig, writeSheet); excelWriter.fill(data(), fillConfig, writeSheet); - Map map = new HashMap(); + Map map = new HashMap(); map.put("date", "2019-10-09 13:28:28"); excelWriter.fill(map, writeSheet); diff --git a/fesod-examples/src/test/resources/fill/dynamicColumn.xlsx b/fesod-examples/src/test/resources/fill/dynamicColumn.xlsx new file mode 100644 index 000000000..f65306048 Binary files /dev/null and b/fesod-examples/src/test/resources/fill/dynamicColumn.xlsx differ diff --git a/fesod-examples/src/test/resources/fill/dynamicColumnObj.xlsx b/fesod-examples/src/test/resources/fill/dynamicColumnObj.xlsx new file mode 100644 index 000000000..12ba8bcbd Binary files /dev/null and b/fesod-examples/src/test/resources/fill/dynamicColumnObj.xlsx differ diff --git a/fesod/src/main/java/org/apache/fesod/excel/annotation/fill/DynamicColumn.java b/fesod/src/main/java/org/apache/fesod/excel/annotation/fill/DynamicColumn.java new file mode 100644 index 000000000..5e8d2c388 --- /dev/null +++ b/fesod/src/main/java/org/apache/fesod/excel/annotation/fill/DynamicColumn.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fesod.excel.annotation.fill; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface DynamicColumn {} diff --git a/fesod/src/main/java/org/apache/fesod/excel/write/executor/AbstractExcelWriteExecutor.java b/fesod/src/main/java/org/apache/fesod/excel/write/executor/AbstractExcelWriteExecutor.java index b6a7f6cce..699dbe34d 100644 --- a/fesod/src/main/java/org/apache/fesod/excel/write/executor/AbstractExcelWriteExecutor.java +++ b/fesod/src/main/java/org/apache/fesod/excel/write/executor/AbstractExcelWriteExecutor.java @@ -19,14 +19,19 @@ package org.apache.fesod.excel.write.executor; +import cn.idev.excel.support.cglib.beans.BeanMap; +import java.lang.reflect.Field; import java.util.List; +import java.util.Map; import org.apache.commons.collections4.CollectionUtils; +import org.apache.fesod.excel.annotation.fill.DynamicColumn; import org.apache.fesod.excel.context.WriteContext; import org.apache.fesod.excel.converters.Converter; import org.apache.fesod.excel.converters.ConverterKeyBuild; import org.apache.fesod.excel.converters.NullableObjectConverter; import org.apache.fesod.excel.converters.WriteConverterContext; import org.apache.fesod.excel.enums.CellDataTypeEnum; +import org.apache.fesod.excel.enums.WriteDirectionEnum; import org.apache.fesod.excel.exception.ExcelWriteDataConvertException; import org.apache.fesod.excel.metadata.data.CommentData; import org.apache.fesod.excel.metadata.data.FormulaData; @@ -35,13 +40,17 @@ import org.apache.fesod.excel.metadata.data.WriteCellData; import org.apache.fesod.excel.metadata.property.ExcelContentProperty; import org.apache.fesod.excel.support.ExcelTypeEnum; +import org.apache.fesod.excel.util.BeanMapUtils; import org.apache.fesod.excel.util.DateUtils; +import org.apache.fesod.excel.util.FieldUtils; import org.apache.fesod.excel.util.FileTypeUtils; import org.apache.fesod.excel.util.ListUtils; import org.apache.fesod.excel.util.StyleUtil; import org.apache.fesod.excel.util.WorkBookUtil; import org.apache.fesod.excel.util.WriteHandlerUtils; import org.apache.fesod.excel.write.handler.context.CellWriteHandlerContext; +import org.apache.fesod.excel.write.metadata.fill.DynamicColumnInfo; +import org.apache.fesod.excel.write.metadata.fill.FillConfig; import org.apache.poi.hssf.usermodel.HSSFClientAnchor; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.ClientAnchor; @@ -66,12 +75,11 @@ public AbstractExcelWriteExecutor(WriteContext writeContext) { } /** - * Transform the data and then to set into the cell + * Transform item * * @param cellWriteHandlerContext context - */ - protected void converterAndSet(CellWriteHandlerContext cellWriteHandlerContext) { - + * */ + protected void convertAndSetItem(CellWriteHandlerContext cellWriteHandlerContext) { WriteCellData cellData = convert(cellWriteHandlerContext); cellWriteHandlerContext.setCellDataList(ListUtils.newArrayList(cellData)); cellWriteHandlerContext.setFirstCellData(cellData); @@ -98,6 +106,10 @@ protected void converterAndSet(CellWriteHandlerContext cellWriteHandlerContext) cellData.setType(CellDataTypeEnum.EMPTY); } Cell cell = cellWriteHandlerContext.getCell(); + setCellValue(cellWriteHandlerContext, cellData, cell); + } + + private void setCellValue(CellWriteHandlerContext cellWriteHandlerContext, WriteCellData cellData, Cell cell) { switch (cellData.getType()) { case STRING: cell.setCellValue(cellData.getStringValue()); @@ -126,6 +138,70 @@ protected void converterAndSet(CellWriteHandlerContext cellWriteHandlerContext) } } + /** + * Transform the data and then to set into the cell + * + * @param cellWriteHandlerContext context + */ + protected void converterAndSet(CellWriteHandlerContext cellWriteHandlerContext) { + Object originalValue = cellWriteHandlerContext.getOriginalValue(); + Field field = cellWriteHandlerContext.getExcelContentProperty().getField(); + if (null != field && field.isAnnotationPresent(DynamicColumn.class)) { + Map dynamicColumnMap = (Map) originalValue; + FillConfig fillConfig = cellWriteHandlerContext.getFillConfig(); + if (null == fillConfig || null == fillConfig.getDynamicColumnInfoMap()) { + throw new ExcelWriteDataConvertException( + cellWriteHandlerContext, + "DynamicColumn annotation must be used with FillConfig.dynamicColumnInfoMap"); + } + DynamicColumnInfo dynamicColumnInfo = fillConfig.getDynamicColumnInfo(field.getName()); + Integer columnIndex = cellWriteHandlerContext.getColumnIndex(); + Integer rowIndex = cellWriteHandlerContext.getRowIndex(); + for (int i = 0; i < dynamicColumnInfo.getKeys().size(); i++) { + String key = dynamicColumnInfo.getKeys().get(i); + Object o = dynamicColumnMap.get(key); + String originalVariable = cellWriteHandlerContext.getOriginalVariable(); + if (originalVariable.contains(".")) { + int dotIndex = originalVariable.indexOf('.'); + if (dotIndex > 0) { + key = originalVariable.substring(dotIndex + 1); + Object itemBean = o; + if (null == itemBean) { + o = null; + } else { + BeanMap beanMap = BeanMapUtils.create(itemBean); + o = beanMap.get(key); + } + } + } + + Integer dynamicColumnGroupSize = dynamicColumnInfo.getGroupSize(); + WriteDirectionEnum direction = fillConfig.getDirection(); + int currentRowIndex = rowIndex; + int currentColumnIndex = columnIndex; + if (WriteDirectionEnum.VERTICAL.equals(direction)) { + currentColumnIndex = columnIndex + dynamicColumnGroupSize * i; + } else { + currentRowIndex = rowIndex + dynamicColumnGroupSize * i; + } + + Map cellMap = cellWriteHandlerContext.getCellMap(); + CellWriteHandlerContext currentCellWriteHandlerContext = + cellMap.get(currentRowIndex + "_" + currentColumnIndex); + currentCellWriteHandlerContext.setOriginalValue(o); + currentCellWriteHandlerContext.setOriginalFieldClass(FieldUtils.getFieldClass(o)); + convertAndSetItem(currentCellWriteHandlerContext); + if (i == 0) { + cellWriteHandlerContext.setOriginalValue(o); + cellWriteHandlerContext.setOriginalFieldClass(FieldUtils.getFieldClass(o)); + convertAndSetItem(cellWriteHandlerContext); + } + } + } else { + convertAndSetItem(cellWriteHandlerContext); + } + } + private void fillFormula(CellWriteHandlerContext cellWriteHandlerContext, FormulaData formulaData) { if (formulaData == null) { return; diff --git a/fesod/src/main/java/org/apache/fesod/excel/write/executor/ExcelWriteFillExecutor.java b/fesod/src/main/java/org/apache/fesod/excel/write/executor/ExcelWriteFillExecutor.java index ab0289dc5..2679f59d9 100644 --- a/fesod/src/main/java/org/apache/fesod/excel/write/executor/ExcelWriteFillExecutor.java +++ b/fesod/src/main/java/org/apache/fesod/excel/write/executor/ExcelWriteFillExecutor.java @@ -19,8 +19,10 @@ package org.apache.fesod.excel.write.executor; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -34,6 +36,7 @@ import lombok.Getter; import lombok.Setter; import org.apache.commons.collections4.CollectionUtils; +import org.apache.fesod.excel.annotation.fill.DynamicColumn; import org.apache.fesod.excel.context.WriteContext; import org.apache.fesod.excel.enums.CellDataTypeEnum; import org.apache.fesod.excel.enums.WriteDirectionEnum; @@ -52,6 +55,7 @@ import org.apache.fesod.excel.write.handler.context.CellWriteHandlerContext; import org.apache.fesod.excel.write.handler.context.RowWriteHandlerContext; import org.apache.fesod.excel.write.metadata.fill.AnalysisCell; +import org.apache.fesod.excel.write.metadata.fill.DynamicColumnInfo; import org.apache.fesod.excel.write.metadata.fill.FillConfig; import org.apache.fesod.excel.write.metadata.fill.FillWrapper; import org.apache.fesod.excel.write.metadata.holder.WriteSheetHolder; @@ -63,8 +67,6 @@ /** * Fill the data into excel - * - * */ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { @@ -74,6 +76,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { private static final String FILL_SUFFIX = "}"; private static final char IGNORE_CHAR = '\\'; private static final String COLLECTION_PREFIX = "."; + private static final String FILL_VARIABLE_SELF = "$"; /** * Fields to replace in the template */ @@ -136,6 +139,7 @@ public void fill(Object data, FillConfig fillConfig) { if (CollectionUtils.isEmpty(collectionData)) { return; } + shiftCellsIfNecessary(fillConfig, collectionData, analysisCellList); Iterator iterator = collectionData.iterator(); if (WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) && fillConfig.getForceNewRow()) { shiftRows(collectionData.size(), analysisCellList); @@ -148,6 +152,57 @@ public void fill(Object data, FillConfig fillConfig) { } } + private void shiftCellsIfNecessary( + FillConfig fillConfig, Collection collectionData, List analysisCellList) { + if (WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) + && CollectionUtils.isNotEmpty(collectionData) + && collectionData.size() > 1) { + Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); + Object item = collectionData.iterator().next(); + Class itemClass = item.getClass(); + Field[] declaredFields = itemClass.getDeclaredFields(); + Map dynamicFieldMap = new HashMap<>(); + for (Field declaredField : declaredFields) { + if (declaredField.isAnnotationPresent(DynamicColumn.class)) { + dynamicFieldMap.put(declaredField.getName(), declaredField); + } + } + analysisCellList.stream() + .sorted(Comparator.comparingInt(AnalysisCell::getColumnIndex) + .reversed()) + .forEach(analysisCell -> { + int rowIndex = analysisCell.getRowIndex(); + int columnIndex = analysisCell.getColumnIndex(); + Row row = sheet.getRow(rowIndex); + List variableList = analysisCell.getVariableList(); + for (String fieldName : dynamicFieldMap.keySet()) { + for (String variable : variableList) { + int dotIndex = variable.indexOf('.'); + if (dotIndex <= 0) { + continue; + } + String variableFieldName = variable.substring(0, dotIndex); + if (StringUtils.equals(fieldName, variableFieldName)) { + List headers = fillConfig + .getDynamicColumnInfo(fieldName) + .getKeys(); + Integer columnGroupSize = fillConfig + .getDynamicColumnInfo(fieldName) + .getGroupSize(); + row.shiftCellsRight( + columnIndex + columnGroupSize, row.getLastCellNum(), headers.size() - 1); + analysisCellList.stream() + .filter(ac -> ac.getColumnIndex() > (columnIndex + columnGroupSize - 1)) + .forEach(ac -> { + ac.setColumnIndex(ac.getColumnIndex() + headers.size() - 1); + }); + } + } + } + }); + } + } + private void shiftRows(int size, List analysisCellList) { if (CollectionUtils.isEmpty(analysisCellList)) { return; @@ -232,9 +287,18 @@ private void doFill( ExcelContentProperty.EMPTY); if (analysisCell.getOnlyOneVariable()) { - String variable = analysisCell.getVariableList().get(0); + String originalVariable = analysisCell.getVariableList().get(0); + String variable = originalVariable; Object value = null; - if (dataKeySet.contains(variable)) { + if (FILL_VARIABLE_SELF.equals(variable)) { + value = oneRowData; + } else if (dataKeySet.contains(variable)) { + value = dataMap.get(variable); + } else if (variable.contains(COLLECTION_PREFIX)) { + int dotIndex = variable.indexOf('.'); + if (dotIndex > 0) { + variable = variable.substring(0, dotIndex); + } value = dataMap.get(variable); } ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty( @@ -245,11 +309,12 @@ private void doFill( .getHeadClazz(), variable, writeContext.currentWriteHolder()); + cellWriteHandlerContext.setOriginalVariable(originalVariable); + cellWriteHandlerContext.setFillConfig(fillConfig); cellWriteHandlerContext.setExcelContentProperty(excelContentProperty); - - createCell(analysisCell, fillConfig, cellWriteHandlerContext, rowWriteHandlerContext); cellWriteHandlerContext.setOriginalValue(value); cellWriteHandlerContext.setOriginalFieldClass(FieldUtils.getFieldClass(dataMap, variable, value)); + createCell(analysisCell, fillConfig, cellWriteHandlerContext, rowWriteHandlerContext); converterAndSet(cellWriteHandlerContext); WriteCellData cellData = cellWriteHandlerContext.getFirstCellData(); @@ -258,7 +323,25 @@ private void doFill( if (fillConfig.getAutoStyle()) { Optional.ofNullable(collectionFieldStyleCache.get(currentUniqueDataFlag)) .map(collectionFieldStyleMap -> collectionFieldStyleMap.get(analysisCell)) - .ifPresent(cellData::setOriginCellStyle); + .ifPresent(style -> { + if (cellWriteHandlerContext.getCellMap() != null + && cellWriteHandlerContext.getCellMap().size() > 1) { + cellWriteHandlerContext + .getCellMap() + .values() + .forEach(cell -> + cell.getFirstCellData().setOriginCellStyle(style)); + } else { + if (fillConfig.getDirection() == WriteDirectionEnum.HORIZONTAL) { + Integer orginColumnIndex = analysisCell.getColumnIndex(); + Sheet sheet = + writeContext.writeSheetHolder().getSheet(); + int columnWidth = sheet.getColumnWidth(orginColumnIndex); + sheet.setColumnWidth(cellWriteHandlerContext.getColumnIndex(), columnWidth); + } + cellData.setOriginCellStyle(style); + } + }); } } else { StringBuilder cellValueBuild = new StringBuilder(); @@ -274,7 +357,9 @@ private void doFill( for (String variable : analysisCell.getVariableList()) { cellValueBuild.append(analysisCell.getPrepareDataList().get(index++)); Object value = null; - if (dataKeySet.contains(variable)) { + if (FILL_VARIABLE_SELF.equals(variable)) { + value = oneRowData; + } else if (dataKeySet.contains(variable)) { value = dataMap.get(variable); } ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty( @@ -324,7 +409,13 @@ private void doFill( .ifPresent(cell::setCellStyle); } } - WriteHandlerUtils.afterCellDispose(cellWriteHandlerContext); + if (cellWriteHandlerContext.getCellMap() != null + && cellWriteHandlerContext.getCellMap().size() > 1) { + // trigger afterCellDispose for every dynamicColumns + cellWriteHandlerContext.getCellMap().values().forEach(WriteHandlerUtils::afterCellDispose); + } else { + WriteHandlerUtils.afterCellDispose(cellWriteHandlerContext); + } } // In the case of the fill line may be called many times @@ -394,6 +485,9 @@ private void createCell( throw new ExcelGenerateException("The wrong direction."); } + ExcelContentProperty excelContentProperty = cellWriteHandlerContext.getExcelContentProperty(); + Field field = excelContentProperty.getField(); + Row row = createRowIfNecessary( sheet, cachedSheet, lastRowIndex, fillConfig, analysisCell, isOriginalCell, rowWriteHandlerContext); cellWriteHandlerContext.setRow(row); @@ -403,10 +497,50 @@ private void createCell( Cell cell = createCellIfNecessary(row, lastColumnIndex, cellWriteHandlerContext); cellWriteHandlerContext.setCell(cell); + if (null != field && field.isAnnotationPresent(DynamicColumn.class)) { + if (cellWriteHandlerContext.getCellMap() == null) { + cellWriteHandlerContext.setCellMap(new HashMap<>()); + } + cellWriteHandlerContext.getCellMap().put(lastRowIndex + "_" + lastColumnIndex, cellWriteHandlerContext); + DynamicColumnInfo dynamicColumnInfo = fillConfig.getDynamicColumnInfo(field.getName()); + if (null == dynamicColumnInfo || CollectionUtils.isEmpty(dynamicColumnInfo.getKeys())) { + throw new ExcelGenerateException( + String.format("Please set dynamic column keys for %s in fillConfig", field.getName())); + } + for (int i = 1; i < dynamicColumnInfo.getKeys().size(); i++) { + switch (fillConfig.getDirection()) { + case VERTICAL: + lastColumnIndex = lastColumnIndex + dynamicColumnInfo.getGroupSize(); + break; + case HORIZONTAL: + lastRowIndex = lastRowIndex + dynamicColumnInfo.getGroupSize(); + break; + default: + throw new ExcelGenerateException("The wrong direction."); + } + Row newRow = createRowIfNecessary( + sheet, cachedSheet, lastRowIndex, fillConfig, analysisCell, false, rowWriteHandlerContext); + CellWriteHandlerContext cloneContext = cellWriteHandlerContext.clone(); + cloneContext.setColumnIndex(lastColumnIndex); + cloneContext.setRowIndex(row.getRowNum()); + Cell cloneCell = createCellIfNecessary(newRow, lastColumnIndex, cloneContext); + cloneContext.setCell(cloneCell); + cellWriteHandlerContext.getCellMap().put(row.getRowNum() + "_" + lastColumnIndex, cloneContext); + } + } + if (isOriginalCell) { Map collectionFieldStyleMap = collectionFieldStyleCache.computeIfAbsent(currentUniqueDataFlag, key -> MapUtils.newHashMap()); collectionFieldStyleMap.put(analysisCell, cell.getCellStyle()); + if (cellWriteHandlerContext.getCellMap() != null + && cellWriteHandlerContext.getCellMap().size() > 1) { + cellWriteHandlerContext.getCellMap().forEach((k, cellContext) -> { + Integer currentColumnIndex = cellContext.getColumnIndex(); + int columnWidth = sheet.getColumnWidth(cellWriteHandlerContext.getColumnIndex()); + sheet.setColumnWidth(currentColumnIndex, columnWidth); + }); + } } } diff --git a/fesod/src/main/java/org/apache/fesod/excel/write/handler/context/CellWriteHandlerContext.java b/fesod/src/main/java/org/apache/fesod/excel/write/handler/context/CellWriteHandlerContext.java index c80232cff..c7b9303d1 100644 --- a/fesod/src/main/java/org/apache/fesod/excel/write/handler/context/CellWriteHandlerContext.java +++ b/fesod/src/main/java/org/apache/fesod/excel/write/handler/context/CellWriteHandlerContext.java @@ -20,6 +20,7 @@ package org.apache.fesod.excel.write.handler.context; import java.util.List; +import java.util.Map; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; @@ -29,6 +30,7 @@ import org.apache.fesod.excel.metadata.data.WriteCellData; import org.apache.fesod.excel.metadata.property.ExcelContentProperty; import org.apache.fesod.excel.write.handler.impl.FillStyleCellWriteHandler; +import org.apache.fesod.excel.write.metadata.fill.FillConfig; import org.apache.fesod.excel.write.metadata.holder.WriteSheetHolder; import org.apache.fesod.excel.write.metadata.holder.WriteTableHolder; import org.apache.fesod.excel.write.metadata.holder.WriteWorkbookHolder; @@ -43,7 +45,7 @@ @Getter @Setter @EqualsAndHashCode -public class CellWriteHandlerContext { +public class CellWriteHandlerContext implements Cloneable { /** * write context */ @@ -72,6 +74,11 @@ public class CellWriteHandlerContext { * cell */ private Cell cell; + /** + * cellMap use by DynamicColumn + * key rowIndex_columnIndex + */ + private Map cellMap; /** * index */ @@ -126,6 +133,35 @@ public class CellWriteHandlerContext { */ private Boolean ignoreFillStyle; + /** + * Fill config + */ + private FillConfig fillConfig; + /** + * Original variable + */ + private String originalVariable; + + @Override + public CellWriteHandlerContext clone() { + CellWriteHandlerContext cellWriteHandlerContext = new CellWriteHandlerContext( + this.writeContext, + this.writeWorkbookHolder, + this.writeSheetHolder, + this.writeTableHolder, + this.row, + this.rowIndex, + this.cell, + this.columnIndex, + this.relativeRowIndex, + this.headData, + this.cellDataList, + this.firstCellData, + this.head, + this.excelContentProperty); + return cellWriteHandlerContext; + } + public CellWriteHandlerContext( WriteContext writeContext, WriteWorkbookHolder writeWorkbookHolder, diff --git a/fesod/src/main/java/org/apache/fesod/excel/write/metadata/fill/DynamicColumnInfo.java b/fesod/src/main/java/org/apache/fesod/excel/write/metadata/fill/DynamicColumnInfo.java new file mode 100644 index 000000000..2bbf62a83 --- /dev/null +++ b/fesod/src/main/java/org/apache/fesod/excel/write/metadata/fill/DynamicColumnInfo.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fesod.excel.write.metadata.fill; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class DynamicColumnInfo { + + /** + * dynamic column keys + * */ + private List keys; + + /** + * dynamic column group size + * */ + private Integer groupSize; +} diff --git a/fesod/src/main/java/org/apache/fesod/excel/write/metadata/fill/FillConfig.java b/fesod/src/main/java/org/apache/fesod/excel/write/metadata/fill/FillConfig.java index f4bab8b18..f68615d6c 100644 --- a/fesod/src/main/java/org/apache/fesod/excel/write/metadata/fill/FillConfig.java +++ b/fesod/src/main/java/org/apache/fesod/excel/write/metadata/fill/FillConfig.java @@ -19,6 +19,9 @@ package org.apache.fesod.excel.write.metadata.fill; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.EqualsAndHashCode; @@ -39,6 +42,7 @@ @NoArgsConstructor @AllArgsConstructor public class FillConfig { + public static final String DEFAULT_DYNAMIC_INFO_KEY = "default"; private WriteDirectionEnum direction; /** * Create a new row each time you use the list parameter.The default create if necessary. @@ -57,6 +61,31 @@ public class FillConfig { private boolean hasInit; + /** + * dynamic column info + * */ + private Map dynamicColumnInfoMap; + + /** + * get dynamic column info + * + * if field name is null or not exist, return default dynamic column info + * else return dynamic column info by field name + * + * @param fieldName field name nullable + * @return dynamic column info + * */ + public DynamicColumnInfo getDynamicColumnInfo(String fieldName) { + if (null == dynamicColumnInfoMap) { + return null; + } + if (null == fieldName || !dynamicColumnInfoMap.containsKey(fieldName)) { + return dynamicColumnInfoMap.get(DEFAULT_DYNAMIC_INFO_KEY); + } else { + return dynamicColumnInfoMap.get(fieldName); + } + } + public void init() { if (hasInit) { return; @@ -72,4 +101,22 @@ public void init() { } hasInit = true; } + + public static class FillConfigBuilder { + public FillConfigBuilder addDynamicInfo(List keys, Integer groupSize, String fieldName) { + if (null == dynamicColumnInfoMap) { + dynamicColumnInfoMap = new HashMap<>(); + } + dynamicColumnInfoMap.put(fieldName, new DynamicColumnInfo(keys, groupSize)); + return this; + } + + public FillConfigBuilder addDefaultDynamicInfo(List keys) { + return addDynamicInfo(keys, 1, DEFAULT_DYNAMIC_INFO_KEY); + } + + public FillConfigBuilder addDefaultDynamicInfo(List keys, Integer groupSize) { + return addDynamicInfo(keys, groupSize, DEFAULT_DYNAMIC_INFO_KEY); + } + } }