All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dromara.hutool.poi.excel.writer.SheetDataWriter Maven / Gradle / Ivy

There is a newer version: 6.0.0.M3
Show newest version
/*
 * 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.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.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;

/**
 * Sheet数据写出器
* 此对象只封装将数据写出到Sheet中,并不刷新到文件 * * @author looly * @since 6.0.0 */ public class SheetDataWriter { private final Sheet sheet; private final ExcelWriteConfig config; private StyleSet styleSet; /** * 标题项对应列号缓存,每次写标题更新此缓存
* 此缓存用于用户多次write时,寻找标题位置 */ private Map headerLocationCache; /** * 当前行,用于标记初始可写数据的行和部分写完后当前的行 */ private final AtomicInteger currentRow; /** * 构造 * * @param sheet {@link Sheet} * @param config Excel配置 * @param styleSet 样式表 */ public SheetDataWriter(final Sheet sheet, final ExcelWriteConfig config, final StyleSet styleSet) { this.sheet = sheet; this.config = config; this.styleSet = styleSet; this.currentRow = new AtomicInteger(0); } /** * 设置样式表 * * @param styleSet 样式表 * @return this */ public SheetDataWriter setStyleSet(final StyleSet styleSet) { this.styleSet = styleSet; 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数据类型不同,写出情况如下: * *
	 * 1、如果为Iterable,直接写出一行
	 * 2、如果为Map,isWriteKeyAsHead为true写出两行,Map的keys做为一行,values做为第二行,否则只写出一行values
	 * 3、如果为Bean,转为Map写出,isWriteKeyAsHead为true写出两行,Map的keys做为一行,values做为第二行,否则只写出一行values
	 * 
* * @param rowBean 写出的Bean * @param isWriteKeyAsHead 为true写出两行,Map的keys做为一行,values做为第二行,否则只写出一行values * @return this * @see #writeRow(Iterable) * @see #writeRow(Map, boolean) * @since 4.1.5 */ @SuppressWarnings({"rawtypes", "unchecked"}) public SheetDataWriter writeRow(final Object rowBean, final boolean isWriteKeyAsHead) { final ExcelWriteConfig config = this.config; final Map rowMap; if (rowBean instanceof Map) { if (MapUtil.isNotEmpty(config.getHeaderAlias())) { rowMap = MapUtil.newTreeMap((Map) rowBean, config.getCachedAliasComparator()); } else { rowMap = (Map) rowBean; } } else if (rowBean instanceof Iterable) { // issue#2398@Github // MapWrapper由于实现了Iterable接口,应该优先按照Map处理 return writeRow((Iterable) rowBean); } else if (rowBean instanceof Hyperlink) { // Hyperlink当成一个值 return writeRow(ListUtil.of(rowBean), isWriteKeyAsHead); } else if (BeanUtil.isReadableBean(rowBean.getClass())) { if (MapUtil.isEmpty(config.getHeaderAlias())) { rowMap = BeanUtil.beanToMap(rowBean, new LinkedHashMap<>(), false, false); } else { // 别名存在情况下按照别名的添加顺序排序Bean数据 rowMap = BeanUtil.beanToMap(rowBean, new TreeMap<>(config.getCachedAliasComparator()), false, false); } } else { // 其它转为字符串默认输出 return writeRow(ListUtil.of(rowBean), isWriteKeyAsHead); } return writeRow(rowMap, isWriteKeyAsHead); } /** * 将一个Map写入到Excel,isWriteKeyAsHead为true写出两行,Map的keys做为一行,values做为第二行,否则只写出一行values
* 如果rowMap为空(包括null),则写出空行 * * @param rowMap 写出的Map,为空(包括null),则写出空行 * @param isWriteKeyAsHead 为true写出两行,Map的keys做为一行,values做为第二行,否则只写出一行values * @return this */ public SheetDataWriter writeRow(final Map rowMap, final boolean isWriteKeyAsHead) { if (MapUtil.isEmpty(rowMap)) { // 如果写出数据为null或空,跳过当前行 passAndGet(); return this; } final Table aliasTable = this.config.aliasTable(rowMap); if (isWriteKeyAsHead) { // 写出标题行,并记录标题别名和列号的关系 writeHeaderRow(aliasTable.columnKeys()); // 记录原数据key和别名对应列号 int i = 0; for (final Object key : aliasTable.rowKeySet()) { this.headerLocationCache.putIfAbsent(StrUtil.toString(key), i); i++; } } // 如果已经写出标题行,根据标题行找对应的值写入 if (MapUtil.isNotEmpty(this.headerLocationCache)) { final Row row = RowUtil.getOrCreateRow(this.sheet, this.currentRow.getAndIncrement()); final CellEditor cellEditor = this.config.getCellEditor(); Integer columnIndex; for (final Table.Cell cell : aliasTable) { columnIndex = getColumnIndex(cell); if (null != columnIndex) { CellUtil.setCellValue(CellUtil.getOrCreateCell(row, columnIndex), cell.getValue(), this.styleSet, false, cellEditor); } } } else { writeRow(aliasTable.values()); } return this; } /** * 写出一行标题数据,标题数据不替换别名
* 本方法只是将数据写入Workbook中的Sheet,并不写出到文件
* 写出的起始行为当前行号,可使用{@link #getCurrentRow()}方法调用,根据写出的的行数,当前行号自动+1 * * @param rowData 一行的数据 * @return this */ public SheetDataWriter writeHeaderRow(final Iterable rowData) { 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); headerLocationCache.put(StrUtil.toString(value), i); i++; } return setHeaderLocationCache(headerLocationCache); } /** * 写出一行数据
* 本方法只是将数据写入Workbook中的Sheet,并不写出到文件
* 写出的起始行为当前行号,可使用{@link #getCurrentRow()}方法调用,根据写出的的行数,当前行号自动+1 * * @param rowData 一行的数据 * @return this */ public SheetDataWriter writeRow(final Iterable rowData) { final int rowNum = this.currentRow.getAndIncrement(); final Row row = this.config.insertRow ? this.sheet.createRow(rowNum) : RowUtil.getOrCreateRow(this.sheet, rowNum); RowUtil.writeRow(row, rowData, this.styleSet, false, this.config.getCellEditor()); return this; } // region ----- currentRow ops /** * 获得当前行 * * @return 当前行 */ public int getCurrentRow() { return this.currentRow.get(); } /** * 设置当前所在行 * * @param rowIndex 行号 * @return this */ public SheetDataWriter setCurrentRow(final int rowIndex) { this.currentRow.set(rowIndex); return this; } /** * 跳过当前行,并获取下一行的行号 * * @return this */ public int passAndGet() { return this.currentRow.incrementAndGet(); } /** * 跳过指定行数,并获取当前行号 * * @param rowNum 跳过的行数 * @return this */ public int passRowsAndGet(final int rowNum) { return this.currentRow.addAndGet(rowNum); } /** * 重置当前行为0 * * @return this */ public SheetDataWriter resetRow() { this.currentRow.set(0); return this; } // endregion /** * 查找标题或标题别名对应的列号 * * @param cell 别名表,rowKey:原名,columnKey:别名 * @return 列号,如果未找到返回null */ private Integer getColumnIndex(final Table.Cell cell) { // 首先查找原名对应的列号 Integer location = this.headerLocationCache.get(StrUtil.toString(cell.getRowKey())); if (null == location) { // 未找到,则查找别名对应的列号 location = this.headerLocationCache.get(StrUtil.toString(cell.getColumnKey())); } return location; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy