add ValueSetter

This commit is contained in:
Looly 2021-08-09 09:35:48 +08:00
parent 26846d0fde
commit b5145a0bac
18 changed files with 437 additions and 92 deletions

View File

@ -3,13 +3,14 @@
-------------------------------------------------------------------------------------------------------------
# 5.7.8 (2021-08-08)
# 5.7.8 (2021-08-09)
### 🐣新特性
* 【core 】 MapProxy支持return this的setter方法pr#392@Gitee
* 【core 】 BeeDSFactory移除sqlite事务修复代码新版本BeeCP已修复
* 【core 】 增加compress包扩充Zip操作灵活性
* 【json 】 增加JSONBeanParser
* 【poi 】 增加CellSetter可以自定义单元格值写出
### 🐞Bug修复
* 【core 】 改进NumberChineseFormatter算法补充完整单元测试解决零问题

View File

@ -3,7 +3,9 @@ package cn.hutool.poi.excel.cell;
import org.apache.poi.ss.usermodel.Cell;
/**
* 单元格编辑器接口
* 单元格编辑器接口<br>
* 在读取Excel值时有时我们需要针对所有单元格统一处理结果值如null转默认值的情况实现接口并调用<br>
* reader.setCellEditor()设置编辑器
*
* @author Looly
*/
@ -11,7 +13,7 @@ import org.apache.poi.ss.usermodel.Cell;
public interface CellEditor {
/**
* 编辑
* 编辑根据单元格信息处理结果值返回处理后的结果
*
* @param cell 单元格对象可以获取单元格行列样式等信息
* @param value 单元格值

View File

@ -3,7 +3,8 @@ package cn.hutool.poi.excel.cell;
import org.apache.poi.ss.usermodel.Cell;
/**
* 单元格处理器接口
* 单元格处理器接口<br>
* 用于在读取Excel单元格值时自定义结果值的获取如在获取值的同时获取单元格样式坐标等信息或根据单元格信息装饰转换结果值
*
* @author Looly
*/

View File

@ -0,0 +1,19 @@
package cn.hutool.poi.excel.cell;
import org.apache.poi.ss.usermodel.Cell;
/**
* 单元格值自定义设置器主要用于Excel数据导出用户通过自定义此接口实现可定制化的单元格值设定
*
* @author looly
* @since 5.7.8
*/
@FunctionalInterface
public interface CellSetter {
/**
* 自定义单元格值设置同时可以设置单元格样式格式等信息
* @param cell 单元格
*/
void setValue(Cell cell);
}

View File

@ -1,11 +1,12 @@
package cn.hutool.poi.excel.cell;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelDateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.StyleSet;
import cn.hutool.poi.excel.cell.setters.CellSetterFactory;
import cn.hutool.poi.excel.cell.values.ErrorCellValue;
import cn.hutool.poi.excel.cell.values.NumericCellValue;
import cn.hutool.poi.excel.editors.TrimEditor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
@ -14,20 +15,14 @@ import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.FormulaError;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.NumberToTextConverter;
import org.apache.poi.ss.util.RegionUtil;
import org.apache.poi.ss.util.SheetUtil;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
@ -118,21 +113,19 @@ public class CellUtil {
Object value;
switch (cellType) {
case NUMERIC:
value = getNumericValue(cell);
value = new NumericCellValue(cell).getValue();
break;
case BOOLEAN:
value = cell.getBooleanCellValue();
break;
case FORMULA:
// 遇到公式时查找公式结果类型
value = getCellValue(cell, cell.getCachedFormulaResultType(), cellEditor);
break;
case BLANK:
value = StrUtil.EMPTY;
break;
case ERROR:
final FormulaError error = FormulaError.forInt(cell.getErrorCellValue());
value = (null == error) ? StrUtil.EMPTY : error.getString();
value = new ErrorCellValue(cell).getValue();
break;
default:
value = cell.getStringCellValue();
@ -193,15 +186,12 @@ public class CellUtil {
* @param style 自定义样式null表示无样式
*/
public static void setCellValue(Cell cell, Object value, CellStyle style) {
if (null == cell) {
return;
}
if (null != style) {
cell.setCellStyle(style);
}
setCellValue(cell, value);
setCellValue(cell, (CellSetter) cell1 -> {
if (null != style) {
cell1.setCellStyle(style);
setCellValue(cell, value);
}
});
}
/**
@ -226,38 +216,7 @@ public class CellUtil {
cell.setBlank();
}
if (null == value) {
cell.setCellValue(StrUtil.EMPTY);
} else if (value instanceof FormulaCellValue) {
// 公式
cell.setCellFormula(((FormulaCellValue) value).getValue());
} else if (value instanceof Date) {
cell.setCellValue((Date) value);
} else if (value instanceof TemporalAccessor) {
if (value instanceof Instant) {
cell.setCellValue(Date.from((Instant) value));
} else if (value instanceof LocalDateTime) {
cell.setCellValue((LocalDateTime) value);
} else if (value instanceof LocalDate) {
cell.setCellValue((LocalDate) value);
}
} else if (value instanceof Calendar) {
cell.setCellValue((Calendar) value);
} else if (value instanceof Boolean) {
cell.setCellValue((Boolean) value);
} else if (value instanceof RichTextString) {
cell.setCellValue((RichTextString) value);
} else if (value instanceof Number) {
// issue https://gitee.com/dromara/hutool/issues/I43U9G
// 避免float到double的精度问题
if (value instanceof Float) {
cell.setCellValue(((Number) value).floatValue());
} else {
cell.setCellValue(((Number) value).doubleValue());
}
} else {
cell.setCellValue(value.toString());
}
CellSetterFactory.createCellSetter(value).setValue(cell);
}
/**
@ -495,36 +454,5 @@ public class CellUtil {
}
return null;
}
/**
* 获取数字类型的单元格值
*
* @param cell 单元格
* @return 单元格值可能为LongDoubleDate
*/
private static Object getNumericValue(Cell cell) {
final double value = cell.getNumericCellValue();
final CellStyle style = cell.getCellStyle();
if (null != style) {
// 判断是否为日期
if (ExcelDateUtil.isDateFormat(cell)) {
return DateUtil.date(cell.getDateCellValue());// 使用Hutool的DateTime包装
}
final String format = style.getDataFormatString();
// 普通数字
if (null != format && format.indexOf(StrUtil.C_DOT) < 0) {
final long longPart = (long) value;
if (((double) longPart) == value) {
// 对于无小数部分的数字类型转为Long
return longPart;
}
}
}
// 某些Excel单元格值为double计算结果可能导致精度问题通过转换解决精度问题
return Double.parseDouble(NumberToTextConverter.toText(value));
}
// -------------------------------------------------------------------------------------------------------------- Private method end
}

View File

@ -2,15 +2,16 @@ package cn.hutool.poi.excel.cell;
/**
* 抽象的单元格值接口用于判断不同类型的单元格值
*
*
* @param <T> 值得类型
* @author looly
* @since 4.0.11
*/
public interface CellValue<T> {
/**
* 获取单元格值
*
*
* @return
*/
T getValue();

View File

@ -1,12 +1,19 @@
package cn.hutool.poi.excel.cell;
import org.apache.poi.ss.usermodel.Cell;
/**
* 公式类型的值
* 公式类型的值<br>
*
* <ul>
* <li>在Sax读取模式时此对象用于接收单元格的公式以及公式结果值信息</li>
* <li>在写出模式时用于定义写出的单元格类型为公式</li>
* </ul>
*
* @author looly
* @since 4.0.11
*/
public class FormulaCellValue implements CellValue<String> {
public class FormulaCellValue implements CellValue<String>, CellSetter {
/**
* 公式
@ -42,6 +49,11 @@ public class FormulaCellValue implements CellValue<String> {
return this.formula;
}
@Override
public void setValue(Cell cell) {
cell.setCellFormula(this.formula);
}
/**
* 获取结果
* @return 结果

View File

@ -0,0 +1,29 @@
package cn.hutool.poi.excel.cell.setters;
import cn.hutool.poi.excel.cell.CellSetter;
import org.apache.poi.ss.usermodel.Cell;
/**
* {@link Boolean} 值单元格设置器
*
* @author looly
* @since 5.7.8
*/
public class BooleanCellSetter implements CellSetter {
private final Boolean value;
/**
* 构造
*
* @param value
*/
BooleanCellSetter(Boolean value) {
this.value = value;
}
@Override
public void setValue(Cell cell) {
cell.setCellValue(value);
}
}

View File

@ -0,0 +1,31 @@
package cn.hutool.poi.excel.cell.setters;
import cn.hutool.poi.excel.cell.CellSetter;
import org.apache.poi.ss.usermodel.Cell;
import java.util.Calendar;
/**
* {@link Calendar} 值单元格设置器
*
* @author looly
* @since 5.7.8
*/
public class CalendarCellSetter implements CellSetter {
private final Calendar value;
/**
* 构造
*
* @param value
*/
CalendarCellSetter(Calendar value) {
this.value = value;
}
@Override
public void setValue(Cell cell) {
cell.setCellValue(value);
}
}

View File

@ -0,0 +1,45 @@
package cn.hutool.poi.excel.cell.setters;
import cn.hutool.poi.excel.cell.CellSetter;
import org.apache.poi.ss.usermodel.RichTextString;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
/**
* {@link CellSetter} 简单静态工厂类用于根据值类型创建对应的{@link CellSetter}
*
* @author looly
* @since 5.7.8
*/
public class CellSetterFactory {
/**
* 创建值对应类型的{@link CellSetter}
*
* @param value
* @return {@link CellSetter}
*/
public static CellSetter createCellSetter(Object value) {
if (null == value) {
return NullCellSetter.INSTANCE;
} else if (value instanceof CellSetter) {
return (CellSetter) value;
} else if (value instanceof Date) {
return new DateCellSetter((Date) value);
} else if (value instanceof TemporalAccessor) {
return new TemporalAccessorCellSetter((TemporalAccessor) value);
} else if (value instanceof Calendar) {
return new CalendarCellSetter((Calendar) value);
} else if (value instanceof Boolean) {
return new BooleanCellSetter((Boolean) value);
} else if (value instanceof RichTextString) {
return new RichTextCellSetter((RichTextString) value);
} else if (value instanceof Number) {
return new NumberCellSetter((Number) value);
} else {
return new CharSequenceCellSetter(value.toString());
}
}
}

View File

@ -0,0 +1,29 @@
package cn.hutool.poi.excel.cell.setters;
import cn.hutool.poi.excel.cell.CellSetter;
import org.apache.poi.ss.usermodel.Cell;
/**
* {@link CharSequence} 值单元格设置器
*
* @author looly
* @since 5.7.8
*/
public class CharSequenceCellSetter implements CellSetter {
private final CharSequence value;
/**
* 构造
*
* @param value
*/
CharSequenceCellSetter(CharSequence value) {
this.value = value;
}
@Override
public void setValue(Cell cell) {
cell.setCellValue(value.toString());
}
}

View File

@ -0,0 +1,31 @@
package cn.hutool.poi.excel.cell.setters;
import cn.hutool.poi.excel.cell.CellSetter;
import org.apache.poi.ss.usermodel.Cell;
import java.util.Date;
/**
* {@link Date} 值单元格设置器
*
* @author looly
* @since 5.7.8
*/
public class DateCellSetter implements CellSetter {
private final Date value;
/**
* 构造
*
* @param value
*/
DateCellSetter(Date value) {
this.value = value;
}
@Override
public void setValue(Cell cell) {
cell.setCellValue(value);
}
}

View File

@ -0,0 +1,21 @@
package cn.hutool.poi.excel.cell.setters;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.cell.CellSetter;
import org.apache.poi.ss.usermodel.Cell;
/**
* {@link Number} 值单元格设置器
*
* @author looly
* @since 5.7.8
*/
public class NullCellSetter implements CellSetter {
public static final NullCellSetter INSTANCE = new NullCellSetter();
@Override
public void setValue(Cell cell) {
cell.setCellValue(StrUtil.EMPTY);
}
}

View File

@ -0,0 +1,35 @@
package cn.hutool.poi.excel.cell.setters;
import cn.hutool.poi.excel.cell.CellSetter;
import org.apache.poi.ss.usermodel.Cell;
/**
* {@link Number} 值单元格设置器
*
* @author looly
* @since 5.7.8
*/
public class NumberCellSetter implements CellSetter {
private final Number value;
/**
* 构造
*
* @param value
*/
NumberCellSetter(Number value) {
this.value = value;
}
@Override
public void setValue(Cell cell) {
// issue https://gitee.com/dromara/hutool/issues/I43U9G
// 避免float到double的精度问题
if (value instanceof Float) {
cell.setCellValue(value.floatValue());
} else {
cell.setCellValue(value.doubleValue());
}
}
}

View File

@ -0,0 +1,30 @@
package cn.hutool.poi.excel.cell.setters;
import cn.hutool.poi.excel.cell.CellSetter;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.RichTextString;
/**
* {@link RichTextString} 值单元格设置器
*
* @author looly
* @since 5.7.8
*/
public class RichTextCellSetter implements CellSetter {
private final RichTextString value;
/**
* 构造
*
* @param value
*/
RichTextCellSetter(RichTextString value) {
this.value = value;
}
@Override
public void setValue(Cell cell) {
cell.setCellValue(value);
}
}

View File

@ -0,0 +1,41 @@
package cn.hutool.poi.excel.cell.setters;
import cn.hutool.poi.excel.cell.CellSetter;
import org.apache.poi.ss.usermodel.Cell;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
/**
* {@link TemporalAccessor} 值单元格设置器
*
* @author looly
* @since 5.7.8
*/
public class TemporalAccessorCellSetter implements CellSetter {
private final TemporalAccessor value;
/**
* 构造
*
* @param value
*/
TemporalAccessorCellSetter(TemporalAccessor value) {
this.value = value;
}
@Override
public void setValue(Cell cell) {
if (value instanceof Instant) {
cell.setCellValue(Date.from((Instant) value));
} else if (value instanceof LocalDateTime) {
cell.setCellValue((LocalDateTime) value);
} else if (value instanceof LocalDate) {
cell.setCellValue((LocalDate) value);
}
}
}

View File

@ -0,0 +1,32 @@
package cn.hutool.poi.excel.cell.values;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.cell.CellValue;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.FormulaError;
/**
* ERROR类型单元格值
*
* @author looly
* @since 5.7.8
*/
public class ErrorCellValue implements CellValue<String> {
private final Cell cell;
/**
* 构造
*
* @param cell {@link Cell}
*/
public ErrorCellValue(Cell cell){
this.cell = cell;
}
@Override
public String getValue() {
final FormulaError error = FormulaError.forInt(cell.getErrorCellValue());
return (null == error) ? StrUtil.EMPTY : error.getString();
}
}

View File

@ -0,0 +1,57 @@
package cn.hutool.poi.excel.cell.values;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelDateUtil;
import cn.hutool.poi.excel.cell.CellValue;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.util.NumberToTextConverter;
/**
* 数字类型单元格值<br>
* 单元格值可能为LongDoubleDate
*
* @author looly
* @since 5.7.8
*/
public class NumericCellValue implements CellValue<Object> {
private final Cell cell;
/**
* 构造
*
* @param cell {@link Cell}
*/
public NumericCellValue(Cell cell){
this.cell = cell;
}
@Override
public Object getValue() {
final double value = cell.getNumericCellValue();
final CellStyle style = cell.getCellStyle();
if (null != style) {
// 判断是否为日期
if (ExcelDateUtil.isDateFormat(cell)) {
// 使用Hutool的DateTime包装
return DateUtil.date(cell.getDateCellValue());
}
final String format = style.getDataFormatString();
// 普通数字
if (null != format && format.indexOf(StrUtil.C_DOT) < 0) {
final long longPart = (long) value;
if (((double) longPart) == value) {
// 对于无小数部分的数字类型转为Long
return longPart;
}
}
}
// 某些Excel单元格值为double计算结果可能导致精度问题通过转换解决精度问题
return Double.parseDouble(NumberToTextConverter.toText(value));
}
}