diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/serialize/GlobalSerializeMapping.java b/hutool-json/src/main/java/org/dromara/hutool/json/serialize/GlobalSerializeMapping.java index 9ca81a3c3..91038bed3 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/serialize/GlobalSerializeMapping.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/serialize/GlobalSerializeMapping.java @@ -97,7 +97,7 @@ public class GlobalSerializeMapping { * @return 自定义的序列化器或者{@code null} */ public static JSONSerializer getSerializer(final Type type) { - if (null == serializerMap) { + if (null == serializerMap || null == type) { return null; } return serializerMap.get(ObjUtil.defaultIfNull(type, NullType.INSTANCE)); @@ -110,7 +110,7 @@ public class GlobalSerializeMapping { * @return 自定义的反序列化器或者{@code null} */ public static JSONDeserializer getDeserializer(final Type type) { - if (null == deserializerMap) { + if (null == deserializerMap || null == type) { return null; } return deserializerMap.get(ObjUtil.defaultIfNull(type, NullType.INSTANCE)); diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/SheetUtil.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/SheetUtil.java index 153b91619..efe8e96b6 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/SheetUtil.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/SheetUtil.java @@ -141,4 +141,21 @@ public class SheetUtil { throw new UnsupportedOperationException("Only XSSFSheet supports addIgnoredErrors"); } } + + /** + * 获取指定坐标点对应的合并单元格范围 + * + * @param sheet {@link Sheet} + * @param x x坐标,即列号 + * @param y 行号 + * @return CellRangeAddress or null + */ + public static CellRangeAddress getMergedRegion(final Sheet sheet, final int x, final int y) { + for (final CellRangeAddress ca : sheet.getMergedRegions()) { + if (ca.isInRange(y, x)) { + return ca; + } + } + return null; + } } diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/CellUtil.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/CellUtil.java index feac78ff6..7463ee93b 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/CellUtil.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/CellUtil.java @@ -19,16 +19,15 @@ package org.dromara.hutool.poi.excel.cell; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellReference; -import org.apache.poi.ss.util.RegionUtil; -import org.apache.poi.ss.util.SheetUtil; -import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.poi.excel.RowUtil; +import org.dromara.hutool.poi.excel.SheetUtil; import org.dromara.hutool.poi.excel.cell.editors.CellEditor; import org.dromara.hutool.poi.excel.cell.editors.TrimEditor; import org.dromara.hutool.poi.excel.cell.setters.CellSetter; import org.dromara.hutool.poi.excel.cell.setters.CellSetterFactory; import org.dromara.hutool.poi.excel.cell.values.CompositeCellValue; import org.dromara.hutool.poi.excel.style.StyleSet; +import org.dromara.hutool.poi.excel.style.StyleUtil; /** * Excel表格中单元格工具类 @@ -194,6 +193,18 @@ public class CellUtil { // region ----- getCell + /** + * 获取或创建指定坐标单元格 + * + * @param sheet {@link Sheet} + * @param x X坐标,从0计数,即列号 + * @param y Y坐标,从0计数,即行号 + * @return {@link Cell} + */ + public static Cell getOrCreateCell(final Sheet sheet, final int x, final int y) { + return getCell(sheet, x, y, true); + } + /** * 获取指定坐标单元格,如果isCreateIfNotExist为false,则在单元格不存在时返回{@code null} * @@ -318,54 +329,33 @@ public class CellUtil { * @return 合并后的单元格号 */ public static int mergingCells(final Sheet sheet, final CellRangeAddress cellRangeAddress, final CellStyle cellStyle) { - setMergeCellStyle(cellStyle, cellRangeAddress, sheet); + if (cellRangeAddress.getNumberOfCells() <= 1) { + // 非合并单元格,无需合并 + return -1; + } + StyleUtil.setBorderStyle(sheet, cellRangeAddress, cellStyle); return sheet.addMergedRegion(cellRangeAddress); } /** - * 获取合并单元格的值
- * 传入的x,y坐标(列行数)可以是合并单元格范围内的任意一个单元格 - * - * @param sheet {@link Sheet} - * @param locationRef 单元格地址标识符,例如A11,B5 - * @return 合并单元格的值 - * @since 5.1.5 - */ - public static Object getMergedRegionValue(final Sheet sheet, final String locationRef) { - final CellReference cellReference = new CellReference(locationRef); - return getMergedRegionValue(sheet, cellReference.getCol(), cellReference.getRow()); - } - - /** - * 获取合并单元格的值
- * 传入的x,y坐标(列行数)可以是合并单元格范围内的任意一个单元格 - * - * @param sheet {@link Sheet} - * @param x 列号,从0开始,可以是合并单元格范围中的任意一列 - * @param y 行号,从0开始,可以是合并单元格范围中的任意一行 - * @return 合并单元格的值 - * @since 4.6.3 - */ - public static Object getMergedRegionValue(final Sheet sheet, final int x, final int y) { - // 合并单元格的识别在getCellValue已经集成,无需重复获取合并单元格 - return getCellValue(SheetUtil.getCell(sheet, x, y)); - } - - /** - * 获取合并单元格
- * 传入的x,y坐标(列行数)可以是合并单元格范围内的任意一个单元格 + * 获取合并单元格中的第一个单元格
+ * 传入的cell可以是合并单元格范围内的任意一个单元格 * * @param cell {@link Cell} * @return 合并单元格 * @since 5.1.5 */ - public static Cell getMergedRegionCell(final Cell cell) { + public static Cell getFirstCellOfMerged(final Cell cell) { if (null == cell) { return null; } - return ObjUtil.defaultIfNull( - getCellIfMergedRegion(cell.getSheet(), cell.getColumnIndex(), cell.getRowIndex()), - cell); + + final MergedCell mergedCell = getMergedCell(cell.getSheet(), cell.getColumnIndex(), cell.getRowIndex()); + if (null != mergedCell) { + return mergedCell.getFirst(); + } + + return cell; } /** @@ -378,10 +368,16 @@ public class CellUtil { * @return 合并单元格,如果非合并单元格,返回坐标对应的单元格 * @since 5.1.5 */ - public static Cell getMergedRegionCell(final Sheet sheet, final int x, final int y) { - return ObjUtil.defaultIfNull( - getCellIfMergedRegion(sheet, x, y), - () -> SheetUtil.getCell(sheet, y, x)); + public static MergedCell getMergedCell(final Sheet sheet, final int x, final int y) { + if (null == sheet) { + return null; + } + + final CellRangeAddress mergedRegion = SheetUtil.getMergedRegion(sheet, x, y); + if (null != mergedRegion) { + return MergedCell.of(getCell(sheet, mergedRegion.getFirstColumn(), mergedRegion.getFirstRow(), false), mergedRegion); + } + return null; } // endregion @@ -440,46 +436,4 @@ public class CellUtil { cell.getRow().removeCell(cell); } } - - // -------------------------------------------------------------------------------------------------------------- Private method start - - /** - * 获取合并单元格,非合并单元格返回{@code null}
- * 传入的x,y坐标(列行数)可以是合并单元格范围内的任意一个单元格 - * - * @param sheet {@link Sheet} - * @param x 列号,从0开始,可以是合并单元格范围中的任意一列 - * @param y 行号,从0开始,可以是合并单元格范围中的任意一行 - * @return 合并单元格,如果非合并单元格,返回{@code null} - * @since 5.4.5 - */ - private static Cell getCellIfMergedRegion(final Sheet sheet, final int x, final int y) { - for (final CellRangeAddress ca : sheet.getMergedRegions()) { - if (ca.isInRange(y, x)) { - return SheetUtil.getCell(sheet, ca.getFirstRow(), ca.getFirstColumn()); - } - } - return null; - } - - /** - * 根据{@link CellStyle}设置合并单元格边框样式 - * - * @param cellStyle {@link CellStyle} - * @param cellRangeAddress {@link CellRangeAddress} - * @param sheet {@link Sheet} - */ - private static void setMergeCellStyle(final CellStyle cellStyle, final CellRangeAddress cellRangeAddress, final Sheet sheet) { - if (null != cellStyle) { - RegionUtil.setBorderTop(cellStyle.getBorderTop(), cellRangeAddress, sheet); - RegionUtil.setBorderRight(cellStyle.getBorderRight(), cellRangeAddress, sheet); - RegionUtil.setBorderBottom(cellStyle.getBorderBottom(), cellRangeAddress, sheet); - RegionUtil.setBorderLeft(cellStyle.getBorderLeft(), cellRangeAddress, sheet); - RegionUtil.setTopBorderColor(cellStyle.getTopBorderColor(), cellRangeAddress, sheet); - RegionUtil.setRightBorderColor(cellStyle.getRightBorderColor(), cellRangeAddress, sheet); - RegionUtil.setLeftBorderColor(cellStyle.getLeftBorderColor(), cellRangeAddress, sheet); - RegionUtil.setBottomBorderColor(cellStyle.getBottomBorderColor(), cellRangeAddress, sheet); - } - } - // -------------------------------------------------------------------------------------------------------------- Private method end } diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/MergedCell.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/MergedCell.java new file mode 100644 index 000000000..940391504 --- /dev/null +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/MergedCell.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2024 Hutool Team and hutool.cn + * + * Licensed 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.dromara.hutool.poi.excel.cell; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.util.CellRangeAddress; + +/** + * 合并单元格封装 + * + * @author Looly + * @since 6.0.0 + */ +public class MergedCell { + + /** + * 创建MergedCell + * + * @param cell 第一个单元格,即左上角的单元格 + * @param rowCount 占用行数 + * @param columnCount 占用列数 + * @return MergedCell + */ + public static MergedCell of(final Cell cell, final int rowCount, final int columnCount) { + final int rowIndex = cell.getRowIndex(); + final int columnIndex = cell.getColumnIndex(); + return of(cell, new CellRangeAddress( + rowIndex, rowIndex + rowCount - 1, + columnIndex, columnIndex + columnCount - 1)); + } + + /** + * 创建MergedCell + * + * @param cell 第一个单元格,即左上角的单元格 + * @param range 合并单元格范围 + * @return MergedCell + */ + public static MergedCell of(final Cell cell, final CellRangeAddress range) { + return new MergedCell(cell, range); + } + + private final Cell first; + private final CellRangeAddress range; + + /** + * 构造 + * + * @param first 第一个单元格,即左上角的单元格 + * @param range 合并单元格范围 + */ + public MergedCell(final Cell first, final CellRangeAddress range) { + this.first = first; + this.range = range; + } + + /** + * 获取第一个单元格,即左上角的单元格 + * + * @return Cell + */ + public Cell getFirst() { + return this.first; + } + + /** + * 获取合并单元格范围 + * + * @return CellRangeAddress + */ + public CellRangeAddress getRange() { + return this.range; + } + + /** + * 设置单元格样式 + * + * @param cellStyle 单元格样式 + * @return this + */ + public MergedCell setCellStyle(final CellStyle cellStyle) { + this.first.setCellStyle(cellStyle); + return this; + } + + /** + * 设置单元格值 + * + * @param value 值 + * @return this + */ + public MergedCell setValue(final Object value) { + CellUtil.setCellValue(this.first, value); + return this; + } +} diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/values/CompositeCellValue.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/values/CompositeCellValue.java index 4c967a615..3fe0fd961 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/values/CompositeCellValue.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/values/CompositeCellValue.java @@ -77,7 +77,7 @@ public class CompositeCellValue implements CellValue { } // 尝试获取合并单元格,如果是合并单元格,则重新获取单元格类型 - final Cell mergedCell = CellUtil.getMergedRegionCell(cell); + final Cell mergedCell = CellUtil.getFirstCellOfMerged(cell); if (mergedCell != cell) { cell = mergedCell; cellType = cell.getCellType(); diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/style/CellBorderStyle.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/style/CellBorderStyle.java index c7605a4af..327c4a0b0 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/style/CellBorderStyle.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/style/CellBorderStyle.java @@ -32,6 +32,24 @@ import java.io.Serializable; public class CellBorderStyle implements Serializable { private static final long serialVersionUID = 1L; + /** + * 根据CellStyle创建单元格边框样式对象。 + * + * @param cellStyle 单元格样式 + * @return CellBorderStyle + */ + public static CellBorderStyle of(final CellStyle cellStyle) { + return new CellBorderStyle() + .setTopStyle(cellStyle.getBorderTop()) + .setTopColor(cellStyle.getTopBorderColor()) + .setRightStyle(cellStyle.getBorderRight()) + .setRightColor(cellStyle.getRightBorderColor()) + .setBottomStyle(cellStyle.getBorderBottom()) + .setBottomColor(cellStyle.getBottomBorderColor()) + .setLeftStyle(cellStyle.getBorderLeft()) + .setLeftColor(cellStyle.getLeftBorderColor()); + } + /** * 创建单元格边框样式对象,四边框样式保持一致。 * @@ -226,7 +244,7 @@ public class CellBorderStyle implements Serializable { * @param cellStyle CellStyle * @return CellStyle */ - public CellStyle setTo(final CellStyle cellStyle){ + public CellStyle setTo(final CellStyle cellStyle) { ObjUtil.accept(this.topStyle, cellStyle::setBorderTop); ObjUtil.accept(this.topColor, cellStyle::setTopBorderColor); diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/style/StyleSet.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/style/StyleSet.java index 4fef47af7..ddec1f15d 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/style/StyleSet.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/style/StyleSet.java @@ -37,7 +37,7 @@ public interface StyleSet { * * @param reference 单元格引用,包含单元格位置等信息 * @param cellValue 单元格值 - * @param isHeader 是否为表头,扁头定义的特殊样式 + * @param isHeader 是否为表头,表头定义的特殊样式 * @return 单元格样式 */ CellStyle getStyleFor(CellReference reference, Object cellValue, boolean isHeader); diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/style/StyleUtil.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/style/StyleUtil.java index 4c0aff239..a300cf722 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/style/StyleUtil.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/style/StyleUtil.java @@ -17,6 +17,8 @@ package org.dromara.hutool.poi.excel.style; import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.RegionUtil; import org.apache.poi.xssf.usermodel.XSSFCellStyle; import org.apache.poi.xssf.usermodel.XSSFColor; import org.dromara.hutool.core.text.StrUtil; @@ -156,6 +158,41 @@ public class StyleUtil { return cellBorderStyle.setTo(cellStyle); } + /** + * 根据{@link CellStyle}设置指定范围边框样式 + * + * @param sheet {@link Sheet} + * @param cellRangeAddress 边框样式范围 + * @param cellBorderStyle 边框风格,包括边框样式、颜色 + */ + public static void setBorderStyle(final Sheet sheet, final CellRangeAddress cellRangeAddress, final CellBorderStyle cellBorderStyle) { + if (null != cellBorderStyle) { + RegionUtil.setBorderTop(cellBorderStyle.getTopStyle(), cellRangeAddress, sheet); + RegionUtil.setBorderRight(cellBorderStyle.getRightStyle(), cellRangeAddress, sheet); + RegionUtil.setBorderBottom(cellBorderStyle.getBottomStyle(), cellRangeAddress, sheet); + RegionUtil.setBorderLeft(cellBorderStyle.getLeftStyle(), cellRangeAddress, sheet); + + RegionUtil.setTopBorderColor(cellBorderStyle.getTopColor(), cellRangeAddress, sheet); + RegionUtil.setRightBorderColor(cellBorderStyle.getRightColor(), cellRangeAddress, sheet); + RegionUtil.setLeftBorderColor(cellBorderStyle.getLeftColor(), cellRangeAddress, sheet); + RegionUtil.setBottomBorderColor(cellBorderStyle.getBottomColor(), cellRangeAddress, sheet); + } + } + + /** + * 根据{@link CellStyle}设置指定范围边框样式 + * + * @param sheet {@link Sheet} + * @param cellRangeAddress {@link CellRangeAddress} + * @param cellStyle {@link CellStyle} + */ + public static void setBorderStyle(final Sheet sheet, final CellRangeAddress cellRangeAddress, final CellStyle cellStyle) { + if (null != cellStyle) { + final CellBorderStyle cellBorderStyle = CellBorderStyle.of(cellStyle); + setBorderStyle(sheet, cellRangeAddress, cellBorderStyle); + } + } + // region ----- color /** diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriter.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriter.java index 54a15d6f4..0babfdf6e 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriter.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriter.java @@ -21,7 +21,6 @@ import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.ss.util.CellReference; import org.dromara.hutool.core.bean.BeanUtil; -import org.dromara.hutool.core.collection.CollUtil; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.io.file.FileUtil; @@ -37,7 +36,6 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.Comparator; -import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -644,9 +642,7 @@ public class ExcelWriter extends ExcelBase { public ExcelWriter merge(final CellRangeAddress cellRangeAddress, final Object content, final CellStyle cellStyle) { checkClosed(); - if(cellRangeAddress.getNumberOfCells() > 1){ - CellUtil.mergingCells(this.getSheet(), cellRangeAddress, cellStyle); - } + CellUtil.mergingCells(this.getSheet(), cellRangeAddress, cellStyle); // 设置内容 if (null != content) { @@ -766,41 +762,9 @@ public class ExcelWriter extends ExcelBase { * @param rowGroup 分组行 * @return this */ - @SuppressWarnings("resource") - public ExcelWriter writeHeader(int x, int y, int rowCount, final RowGroup rowGroup) { - - // 写主标题 - final String name = rowGroup.getName(); - final List children = rowGroup.getChildren(); - if (null != name) { - if(!CollUtil.isEmpty(children)){ - // 有子节点,标题行只占用除子节点占用的行数 - rowCount = Math.max(1, rowCount - rowGroup.childrenMaxRowCount()); - //nameRowCount = 1; - } - - // 如果无子节点,则标题行占用所有行 - final CellRangeAddress cellAddresses = CellRangeUtil.of(y, y + rowCount - 1, x, x + rowGroup.maxColumnCount() - 1); - final CellStyle style = rowGroup.getStyle(); - if(null == style){ - merge(cellAddresses, name, true); - } else{ - merge(cellAddresses, name, style); - } - // 子分组写到下N行 - y += rowCount; - } - - // 写分组 - final int childrenMaxRowCount = rowGroup.childrenMaxRowCount(); - if(childrenMaxRowCount > 0){ - for (final RowGroup child : children) { - // 子分组行高填充为当前分组最大值 - writeHeader(x, y, childrenMaxRowCount, child); - x += child.maxColumnCount(); - } - } - + public ExcelWriter writeHeader(final int x, final int y, final int rowCount, final RowGroup rowGroup) { + checkClosed(); + this.getSheetDataWriter().writeHeader(x, y, rowCount, rowGroup); return this; } // endregion diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetDataWriter.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetDataWriter.java index 0f90353e9..22f29be4c 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetDataWriter.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetDataWriter.java @@ -18,20 +18,26 @@ package org.dromara.hutool.poi.excel.writer; import org.apache.poi.common.usermodel.Hyperlink; import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellReference; import org.dromara.hutool.core.bean.BeanUtil; +import org.dromara.hutool.core.collection.CollUtil; import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.map.MapUtil; -import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap; import org.dromara.hutool.core.map.multi.Table; import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.poi.excel.RowGroup; import org.dromara.hutool.poi.excel.RowUtil; +import org.dromara.hutool.poi.excel.cell.CellRangeUtil; import org.dromara.hutool.poi.excel.cell.CellUtil; import org.dromara.hutool.poi.excel.cell.editors.CellEditor; import org.dromara.hutool.poi.excel.style.StyleSet; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicInteger; @@ -53,7 +59,7 @@ public class SheetDataWriter { * 标题项对应列号缓存,每次写标题更新此缓存
* 此缓存用于用户多次write时,寻找标题位置 */ - private Map headLocationCache; + private Map headerLocationCache; /** * 当前行,用于标记初始可写数据的行和部分写完后当前的行 */ @@ -75,6 +81,7 @@ public class SheetDataWriter { /** * 设置样式表 + * * @param styleSet 样式表 * @return this */ @@ -83,6 +90,67 @@ public class SheetDataWriter { return this; } + /** + * 设置标题位置映射缓存 + * + * @param headerLocationCache 标题位置映射缓存,key为表明名,value为列号 + * @return this + */ + public SheetDataWriter setHeaderLocationCache(final Map headerLocationCache) { + this.headerLocationCache = headerLocationCache; + return this; + } + + /** + * 写出分组标题行 + * + * @param x 开始的列,下标从0开始 + * @param y 开始的行,下标从0开始 + * @param rowCount 当前分组行所占行数,此数值为标题占用行数+子分组占用的最大行数,不确定传1 + * @param rowGroup 分组行 + * @return this + */ + public SheetDataWriter writeHeader(int x, int y, int rowCount, final RowGroup rowGroup) { + + // 写主标题 + final String name = rowGroup.getName(); + final List children = rowGroup.getChildren(); + if (null != name) { + if(CollUtil.isNotEmpty(children)){ + // 有子节点,标题行只占用除子节点占用的行数 + rowCount = Math.max(1, rowCount - rowGroup.childrenMaxRowCount()); + //nameRowCount = 1; + } + + // 如果无子节点,则标题行占用所有行 + final CellRangeAddress cellRangeAddresses = CellRangeUtil.of(y, y + rowCount - 1, x, x + rowGroup.maxColumnCount() - 1); + CellStyle style = rowGroup.getStyle(); + if (null == style && null != this.styleSet) { + style = styleSet.getStyleFor(new CellReference(cellRangeAddresses.getFirstRow(), cellRangeAddresses.getFirstColumn()), name, true); + } + CellUtil.mergingCells(this.sheet, cellRangeAddresses, style); + final Cell cell = CellUtil.getOrCreateCell(this.sheet, cellRangeAddresses.getFirstColumn(), cellRangeAddresses.getFirstRow()); + if(null != cell){ + CellUtil.setCellValue(cell, name, style, this.config.getCellEditor()); + } + + // 子分组写到下N行 + y += rowCount; + } + + // 写分组 + final int childrenMaxRowCount = rowGroup.childrenMaxRowCount(); + if(childrenMaxRowCount > 0){ + for (final RowGroup child : children) { + // 子分组行高填充为当前分组最大值 + writeHeader(x, y, childrenMaxRowCount, child); + x += child.maxColumnCount(); + } + } + + return this; + } + /** * 写出一行,根据rowBean数据类型不同,写出情况如下: * @@ -153,13 +221,13 @@ public class SheetDataWriter { // 记录原数据key和别名对应列号 int i = 0; for (final Object key : aliasTable.rowKeySet()) { - this.headLocationCache.putIfAbsent(StrUtil.toString(key), i); + this.headerLocationCache.putIfAbsent(StrUtil.toString(key), i); i++; } } // 如果已经写出标题行,根据标题行找对应的值写入 - if (MapUtil.isNotEmpty(this.headLocationCache)) { + if (MapUtil.isNotEmpty(this.headerLocationCache)) { final Row row = RowUtil.getOrCreateRow(this.sheet, this.currentRow.getAndIncrement()); final CellEditor cellEditor = this.config.getCellEditor(); Integer columnIndex; @@ -176,7 +244,7 @@ public class SheetDataWriter { } /** - * 写出一行标题数据
+ * 写出一行标题数据,标题数据不替换别名
* 本方法只是将数据写入Workbook中的Sheet,并不写出到文件
* 写出的起始行为当前行号,可使用{@link #getCurrentRow()}方法调用,根据写出的的行数,当前行号自动+1 * @@ -184,21 +252,20 @@ public class SheetDataWriter { * @return this */ public SheetDataWriter writeHeaderRow(final Iterable rowData) { - this.headLocationCache = new SafeConcurrentHashMap<>(); - final int rowNum = this.currentRow.getAndIncrement(); final Row row = this.config.insertRow ? this.sheet.createRow(rowNum) : RowUtil.getOrCreateRow(this.sheet, rowNum); + final Map headerLocationCache = new LinkedHashMap<>(); final CellEditor cellEditor = this.config.getCellEditor(); int i = 0; Cell cell; for (final Object value : rowData) { cell = CellUtil.getOrCreateCell(row, i); CellUtil.setCellValue(cell, value, this.styleSet, true, cellEditor); - this.headLocationCache.put(StrUtil.toString(value), i); + headerLocationCache.put(StrUtil.toString(value), i); i++; } - return this; + return setHeaderLocationCache(headerLocationCache); } /** @@ -217,6 +284,7 @@ public class SheetDataWriter { } // region ----- currentRow ops + /** * 获得当前行 * @@ -275,10 +343,10 @@ public class SheetDataWriter { */ private Integer getColumnIndex(final Table.Cell cell) { // 首先查找原名对应的列号 - Integer location = this.headLocationCache.get(StrUtil.toString(cell.getRowKey())); + Integer location = this.headerLocationCache.get(StrUtil.toString(cell.getRowKey())); if (null == location) { // 未找到,则查找别名对应的列号 - location = this.headLocationCache.get(StrUtil.toString(cell.getColumnKey())); + location = this.headerLocationCache.get(StrUtil.toString(cell.getColumnKey())); } return location; } diff --git a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/ExcelWriteTest.java b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/ExcelWriteTest.java index 2519f00db..46271c9ff 100644 --- a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/ExcelWriteTest.java +++ b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/ExcelWriteTest.java @@ -213,6 +213,17 @@ public class ExcelWriteTest { writer.close(); } + @Test + @Disabled + public void mergeNotExistCellTest(){ + final ExcelWriter writer = ExcelUtil.getWriter() + .merge(5); + + writer + .flush(FileUtil.file("d:/test/mergeNotExist.xlsx"), true) + .close(); + } + @Test @Disabled public void mergeTest2() {