diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java
index 9f05be414..5b6f41161 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java
@@ -195,6 +195,15 @@ public class BeanUtil {
if (null == bean || StrUtil.isBlank(expression)) {
return null;
}
+
+ // 先尝试直接获取属性
+ if (bean instanceof Map) {
+ final Map, ?> map = (Map, ?>) bean;
+ if(map.containsKey(expression)){
+ return (T) map.get(expression);
+ }
+ }
+
return (T) BeanPath.of(expression).getValue(bean);
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/BeanPath.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/BeanPath.java
index f91831d78..97bb1242a 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/BeanPath.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/BeanPath.java
@@ -164,6 +164,9 @@ public class BeanPath implements Iterator {
*/
public Object getValue(final Object bean) {
final Object value = this.node.getValue(bean);
+ if(null == value){
+ return null;
+ }
if (!hasNext()) {
return value;
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/node/NameNode.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/node/NameNode.java
index 574c049ef..b697b2817 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/node/NameNode.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/node/NameNode.java
@@ -52,6 +52,9 @@ public class NameNode implements Node {
@Override
public Object getValue(final Object bean) {
+ if(null == bean){
+ return null;
+ }
if ("$".equals(name)) {
return bean;
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java
index 71e0d7339..31b17caf9 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java
@@ -43,10 +43,7 @@ import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.text.MessageFormat;
import java.text.Normalizer;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
import java.util.function.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -2485,6 +2482,46 @@ public class CharSequenceUtil extends StrValidator {
public static String indexedFormat(final CharSequence pattern, final Object... arguments) {
return MessageFormat.format(pattern.toString(), arguments);
}
+
+ /**
+ * 格式化文本,使用 {varName} 占位
+ * map = {a: "aValue", b: "bValue"} format("{a} and {b}", map) ---=》 aValue and bValue
+ *
+ * @param template 文本模板,被替换的部分用 {key} 表示
+ * @param map 参数值对
+ * @return 格式化后的文本
+ */
+ public static String formatByMap(final CharSequence template, final Map, ?> map) {
+ return formatByMap(template, map, true);
+ }
+
+ /**
+ * 格式化文本,使用 {varName} 占位
+ * map = {a: "aValue", b: "bValue"} format("{a} and {b}", map) ---=》 aValue and bValue
+ *
+ * @param template 文本模板,被替换的部分用 {key} 表示
+ * @param map 参数值对
+ * @param ignoreNull 是否忽略 {@code null} 值,忽略则 {@code null} 值对应的变量不被替换,否则替换为""
+ * @return 格式化后的文本
+ * @since 5.4.3
+ */
+ public static String formatByMap(final CharSequence template, final Map, ?> map, final boolean ignoreNull) {
+ return StrFormatter.formatByBean(template, map, ignoreNull);
+ }
+
+ /**
+ * 格式化文本,使用 {varName} 占位
+ * bean = User:{a: "aValue", b: "bValue"} format("{a} and {b}", bean) ---=》 aValue and bValue
+ *
+ * @param template 文本模板,被替换的部分用 {key} 表示
+ * @param bean 参数Bean
+ * @param ignoreNull 是否忽略 {@code null} 值,忽略则 {@code null} 值对应的变量不被替换,否则替换为""
+ * @return 格式化后的文本
+ * @since 5.4.3
+ */
+ public static String formatByBean(final CharSequence template, final Object bean, final boolean ignoreNull) {
+ return StrFormatter.formatByBean(template, bean, ignoreNull);
+ }
// endregion
// region ----- wrap
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/StrUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/StrUtil.java
index 003cad0f7..3b07ecc7b 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/text/StrUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/StrUtil.java
@@ -18,7 +18,6 @@ package org.dromara.hutool.core.text;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.func.FunctionPool;
-import org.dromara.hutool.core.text.placeholder.StrFormatter;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.dromara.hutool.core.util.CharsetUtil;
@@ -26,7 +25,6 @@ import java.io.StringReader;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
-import java.util.Map;
/**
* 字符串工具类
@@ -36,10 +34,10 @@ import java.util.Map;
* 字符串分割split参考:{@link SplitUtil}
* 多字符串判空hasBlank参考:{@link ArrayUtil}
*
- * @see SplitUtil#split(CharSequence, CharSequence) 对字符串分割
- * @see ArrayUtil#hasBlank(CharSequence...) 对多个字符串判空
*
* @author Looly
+ * @see SplitUtil#split(CharSequence, CharSequence) 对字符串分割
+ * @see ArrayUtil#hasBlank(CharSequence...) 对多个字符串判空
*/
public class StrUtil extends CharSequenceUtil implements StrPool {
@@ -130,6 +128,7 @@ public class StrUtil extends CharSequenceUtil implements StrPool {
}
// region ----- str
+
/**
* 将对象转为字符串
*
@@ -164,9 +163,9 @@ public class StrUtil extends CharSequenceUtil implements StrPool {
if (obj instanceof String) {
return (String) obj;
- }else if(obj instanceof char[]){
+ } else if (obj instanceof char[]) {
return new String((char[]) obj);
- }else if (obj instanceof byte[]) {
+ } else if (obj instanceof byte[]) {
return str((byte[]) obj, charset);
} else if (obj instanceof Byte[]) {
return str((Byte[]) obj, charset);
@@ -240,7 +239,7 @@ public class StrUtil extends CharSequenceUtil implements StrPool {
* @param value char[]值,注意这个数组不可修改!!
* @return String
*/
- public static String strFast(final char[] value){
+ public static String strFast(final char[] value) {
return FunctionPool.createString(value);
}
// endregion
@@ -372,30 +371,4 @@ public class StrUtil extends CharSequenceUtil implements StrPool {
public static String similar(final String str1, final String str2, final int scale) {
return TextSimilarity.similar(str1, str2, scale);
}
-
- /**
- * 格式化文本,使用 {varName} 占位
- * map = {a: "aValue", b: "bValue"} format("{a} and {b}", map) ---=》 aValue and bValue
- *
- * @param template 文本模板,被替换的部分用 {key} 表示
- * @param map 参数值对
- * @return 格式化后的文本
- */
- public static String format(final CharSequence template, final Map, ?> map) {
- return format(template, map, true);
- }
-
- /**
- * 格式化文本,使用 {varName} 占位
- * map = {a: "aValue", b: "bValue"} format("{a} and {b}", map) ---=》 aValue and bValue
- *
- * @param template 文本模板,被替换的部分用 {key} 表示
- * @param map 参数值对
- * @param ignoreNull 是否忽略 {@code null} 值,忽略则 {@code null} 值对应的变量不被替换,否则替换为""
- * @return 格式化后的文本
- * @since 5.4.3
- */
- public static String format(final CharSequence template, final Map, ?> map, final boolean ignoreNull) {
- return StrFormatter.format(template, map, ignoreNull);
- }
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/StrFormatter.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/StrFormatter.java
index 39206acb8..bf3c9ec7f 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/StrFormatter.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/StrFormatter.java
@@ -18,6 +18,7 @@ package org.dromara.hutool.core.text.placeholder;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.lang.mutable.MutableEntry;
+import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.map.reference.WeakConcurrentMap;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.placeholder.template.NamedPlaceholderStrTemplate;
@@ -70,27 +71,31 @@ public class StrFormatter {
return strPattern;
}
return ((SinglePlaceholderStrTemplate) CACHE.computeIfAbsent(MutableEntry.of(strPattern, placeHolder), k ->
- StrTemplate.of(strPattern).placeholder(placeHolder).build()))
- .format(argArray);
+ StrTemplate.of(strPattern).placeholder(placeHolder).build()))
+ .format(argArray);
}
/**
* 格式化文本,使用 {varName} 占位
- * map = {a: "aValue", b: "bValue"} format("{a} and {b}", map) ---=》 aValue and bValue
+ * bean = User:{a: "aValue", b: "bValue"} format("{a} and {b}", bean) ---=》 aValue and bValue
*
* @param template 文本模板,被替换的部分用 {key} 表示
- * @param map 参数值对
+ * @param bean 参数Bean
* @param ignoreNull 是否忽略 {@code null} 值,忽略则 {@code null} 值对应的变量不被替换,否则替换为""
* @return 格式化后的文本
- * @since 5.7.10
+ * @since 6.0.0
*/
- public static String format(final CharSequence template, final Map, ?> map, final boolean ignoreNull) {
+ public static String formatByBean(final CharSequence template, final Object bean, final boolean ignoreNull) {
if (null == template) {
return null;
}
- if (null == map || map.isEmpty()) {
- return template.toString();
+
+ if (bean instanceof Map) {
+ if (MapUtil.isEmpty((Map, ?>) bean)) {
+ return template.toString();
+ }
}
+ // Bean的空检查需要反射,性能很差,此处不检查
return ((NamedPlaceholderStrTemplate) CACHE.computeIfAbsent(MutableEntry.of(template, ignoreNull), k -> {
final NamedPlaceholderStrTemplate.Builder builder = StrTemplate.ofNamed(template.toString());
@@ -100,7 +105,7 @@ public class StrFormatter {
builder.addFeatures(StrTemplate.Feature.FORMAT_NULL_VALUE_TO_EMPTY);
}
return builder.build();
- })).format(map);
+ })).format(bean);
}
/**
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/template/NamedPlaceholderStrTemplate.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/template/NamedPlaceholderStrTemplate.java
index faf6d12ea..c5d0a0347 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/template/NamedPlaceholderStrTemplate.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/template/NamedPlaceholderStrTemplate.java
@@ -22,6 +22,7 @@ import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.exception.HutoolException;
import org.dromara.hutool.core.lang.Assert;
+import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.core.text.StrPool;
import org.dromara.hutool.core.text.placeholder.StrTemplate;
@@ -353,7 +354,7 @@ public class NamedPlaceholderStrTemplate extends StrTemplate {
* @return 格式化字符串
*/
public String format(final Map map) {
- if (map == null) {
+ if (MapUtil.isEmpty(map)) {
return getTemplate();
}
return format(map::get, map::containsKey);
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/text/StrFormatterTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/text/StrFormatterTest.java
index deef09683..411e99d08 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/text/StrFormatterTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/text/StrFormatterTest.java
@@ -12,6 +12,8 @@
package org.dromara.hutool.core.text;
+import lombok.AllArgsConstructor;
+import lombok.Data;
import org.dromara.hutool.core.text.placeholder.StrFormatter;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -62,4 +64,20 @@ public class StrFormatterTest {
final String result3 = StrFormatter.formatWith("this is \\\\$$$ for $$$", "$$$", "a", "b");
Assertions.assertEquals("this is \\a for b", result3);
}
+
+ @Test
+ void formatByBeanTest() {
+ final User user = new User("张三", 18, true);
+ final String s = StrFormatter.formatByBean("User name: {name}, age: {age}, gender: {gender}", user, true);
+
+ Assertions.assertEquals("User name: 张三, age: 18, gender: true", s);
+ }
+
+ @Data
+ @AllArgsConstructor
+ private static class User{
+ private String name;
+ private int age;
+ private boolean gender;
+ }
}
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/text/StrUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/text/StrUtilTest.java
index 9a325b23c..5e90acb07 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/text/StrUtilTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/text/StrUtilTest.java
@@ -89,10 +89,10 @@ public class StrUtilTest {
@Test
public void formatTest() {
final String template = "你好,我是{name},我的电话是:{phone}";
- final String result = StrUtil.format(template, Dict.of().set("name", "张三").set("phone", "13888881111"));
+ final String result = StrUtil.formatByMap(template, Dict.of().set("name", "张三").set("phone", "13888881111"));
Assertions.assertEquals("你好,我是张三,我的电话是:13888881111", result);
- final String result2 = StrUtil.format(template, Dict.of().set("name", "张三").set("phone", null));
+ final String result2 = StrUtil.formatByMap(template, Dict.of().set("name", "张三").set("phone", null));
Assertions.assertEquals("你好,我是张三,我的电话是:{phone}", result2);
}
diff --git a/hutool-log/src/main/java/org/dromara/hutool/log/engine/console/ConsoleLog.java b/hutool-log/src/main/java/org/dromara/hutool/log/engine/console/ConsoleLog.java
index 5aaa76d16..a3a394961 100644
--- a/hutool-log/src/main/java/org/dromara/hutool/log/engine/console/ConsoleLog.java
+++ b/hutool-log/src/main/java/org/dromara/hutool/log/engine/console/ConsoleLog.java
@@ -143,7 +143,7 @@ public class ConsoleLog extends AbstractLog {
.set("name", this.name)
.set("msg", StrUtil.format(format, arguments));
- final String logMsg = StrUtil.format(logFormat, dict);
+ final String logMsg = StrUtil.formatByMap(logFormat, dict);
//WARN以上级别打印至System.err
if (level.ordinal() >= Level.WARN.ordinal()) {
diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/VirtualCell.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/VirtualCell.java
index 0d5c570c1..fac6d74dc 100644
--- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/VirtualCell.java
+++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/cell/VirtualCell.java
@@ -41,6 +41,7 @@ public class VirtualCell extends CellBase {
private CellType cellType;
private Object value;
private CellStyle style;
+ private Comment comment;
/**
* 构造
@@ -57,7 +58,20 @@ public class VirtualCell extends CellBase {
this.comment = cell.getCellComment();
}
- private Comment comment;
+ /**
+ * 构造
+ *
+ * @param cell 参照单元格
+ * @param x 新的列号,从0开始
+ * @param y 新的行号,从0开始
+ * @param value 新值
+ */
+ public VirtualCell(final Cell cell, final int x, final int y, final Object value) {
+ this(cell.getRow(), x, y);
+ this.style = cell.getCellStyle();
+ this.comment = cell.getCellComment();
+ CellUtil.setCellValue(this, value);
+ }
/**
* 构造
@@ -91,31 +105,37 @@ public class VirtualCell extends CellBase {
@Override
protected void setCellValueImpl(final double value) {
+ this.cellType = CellType.NUMERIC;
this.value = value;
}
@Override
protected void setCellValueImpl(final Date value) {
+ this.cellType = CellType.NUMERIC;
this.value = value;
}
@Override
protected void setCellValueImpl(final LocalDateTime value) {
+ this.cellType = CellType.NUMERIC;
this.value = value;
}
@Override
protected void setCellValueImpl(final Calendar value) {
+ this.cellType = CellType.NUMERIC;
this.value = value;
}
@Override
protected void setCellValueImpl(final String value) {
+ this.cellType = CellType.STRING;
this.value = value;
}
@Override
protected void setCellValueImpl(final RichTextString value) {
+ this.cellType = CellType.STRING;
this.value = value;
}
@@ -192,11 +212,13 @@ public class VirtualCell extends CellBase {
@Override
public void setCellValue(final boolean value) {
+ this.cellType = CellType.BOOLEAN;
this.value = value;
}
@Override
public void setCellErrorValue(final byte value) {
+ this.cellType = CellType.ERROR;
this.value = value;
}
diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetTemplateWriter.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetTemplateWriter.java
index ad9059957..f020dfcd5 100644
--- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetTemplateWriter.java
+++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetTemplateWriter.java
@@ -18,7 +18,6 @@ package org.dromara.hutool.poi.excel.writer;
import org.apache.poi.ss.usermodel.Sheet;
import org.dromara.hutool.core.map.BeanMap;
-import org.dromara.hutool.core.text.StrUtil;
import java.util.Map;
@@ -57,31 +56,20 @@ public class SheetTemplateWriter {
* @return this
*/
public SheetTemplateWriter fillOnce(final Map, ?> rowMap) {
- rowMap.forEach((key, value) -> this.templateContext.fill(StrUtil.toStringOrNull(key), rowMap, false));
+ this.templateContext.fill(rowMap, false);
return this;
}
/**
* 填充模板行,用于列表填充
*
- * @param rowBean 行的Bean数据
+ * @param rowBean 行的Bean或Map数据
* @return this
*/
public SheetTemplateWriter fillRow(final Object rowBean) {
- // TODO 支持Bean的级联属性获取
- return fillRow(new BeanMap(rowBean));
- }
-
- /**
- * 填充模板行,用于列表填充
- *
- * @param rowMap 行数据
- * @return this
- */
- public SheetTemplateWriter fillRow(final Map, ?> rowMap) {
if (this.config.insertRow) {
// 当前填充行的模板行以下全部下移
- final int bottomRowIndex = this.templateContext.getBottomRowIndex(rowMap);
+ final int bottomRowIndex = this.templateContext.getBottomRowIndex(rowBean);
if (bottomRowIndex < 0) {
// 无可填充行
return this;
@@ -96,7 +84,7 @@ public class SheetTemplateWriter {
}
}
- rowMap.forEach((key, value) -> this.templateContext.fill(StrUtil.toStringOrNull(key), rowMap, true));
+ this.templateContext.fill(rowBean, true);
return this;
}
diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/TemplateContext.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/TemplateContext.java
index b85d4c0c7..daa5bc11e 100644
--- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/TemplateContext.java
+++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/TemplateContext.java
@@ -19,11 +19,14 @@ package org.dromara.hutool.poi.excel.writer;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Sheet;
+import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.lang.Assert;
+import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.regex.ReUtil;
import org.dromara.hutool.core.text.StrPool;
import org.dromara.hutool.core.text.StrUtil;
+import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.poi.excel.SheetUtil;
import org.dromara.hutool.poi.excel.cell.CellUtil;
import org.dromara.hutool.poi.excel.cell.VirtualCell;
@@ -31,6 +34,7 @@ import org.dromara.hutool.poi.excel.cell.VirtualCell;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
/**
@@ -86,54 +90,71 @@ public class TemplateContext {
* 如果为{@link VirtualCell},返回最底部虚拟单元格各行号
*
*
- * @param rowData 填充数据
+ * @param rowDataBean 填充数据
* @return 最大行索引,-1表示无数据填充,0表示无需下移
*/
- public int getBottomRowIndex(final Map, ?> rowData) {
- int bottomRowIndex = -1;
- Cell cell;
- for (final Object key : rowData.keySet()) {
- cell = this.varMap.get(StrUtil.toStringOrNull(key));
- if (null != cell) {
- if(cell instanceof VirtualCell){
- bottomRowIndex = Math.max(bottomRowIndex, cell.getRowIndex());
- } else{
+ public int getBottomRowIndex(final Object rowDataBean) {
+ final AtomicInteger bottomRowIndex = new AtomicInteger(-1);
+ this.varMap.forEach((name, cell) -> {
+ if(null != BeanUtil.getProperty(rowDataBean, name)){
+ if (cell instanceof VirtualCell) {
+ bottomRowIndex.set(Math.max(bottomRowIndex.get(), cell.getRowIndex()));
+ } else if(bottomRowIndex.get() < 0){
// 实体单元格,直接填充,无需下移
- bottomRowIndex = 0;
+ bottomRowIndex.set(0);
}
}
- }
- return bottomRowIndex;
+ });
+ return bottomRowIndex.get();
}
/**
* 填充变量名name指向的单元格
*
- * @param name 变量名
- * @param rowData 一行数据的键值对
- * @param isListVar 是否为列表填充,列表填充会自动指向下一列,否则填充结束后删除变量
+ * @param rowDataBean 一行数据的键值对
+ * @param isListVar 是否为列表填充,列表填充会自动指向下一列,否则填充结束后删除变量
* @since 6.0.0
*/
- public void fill(final String name, final Map, ?> rowData, final boolean isListVar) {
- final Cell cell = varMap.get(name);
- if (null == cell) {
- // 没有对应变量占位
- return;
+ public void fill(final Object rowDataBean, final boolean isListVar) {
+ final Map varMap = this.varMap;
+ varMap.forEach((name, cell) -> {
+ if (null == cell) {
+ return;
+ }
+
+ final String templateStr = cell.getStringCellValue();
+ // 填充单元格
+ if (fill(cell, name, rowDataBean)) {
+ // 指向下一个单元格
+ putNext(name, cell, templateStr, isListVar);
+ }
+ });
+
+ if (!isListVar) {
+ // 清理已经匹配完毕的变量
+ MapUtil.removeNullValue(varMap);
}
+ }
- final String templateStr = cell.getStringCellValue();
-
+ /**
+ * 将变量指向下一行的单元格
+ * 如果为列表,则指向下一行的虚拟单元格(不创建单元格)
+ * 如果非列表,则清空此变量
+ *
+ * @param name 变量名
+ * @param currentCell 当前单元格
+ * @param templateStr 模板字符串
+ * @param isListVar 是否为列表填充
+ */
+ private void putNext(final String name, final Cell currentCell, final String templateStr, final boolean isListVar) {
if (isListVar) {
// 指向下一列的单元格
- final Cell next = new VirtualCell(cell, cell.getColumnIndex(), cell.getRowIndex() + 1);
- next.setCellValue(templateStr);
+ final Cell next = new VirtualCell(currentCell, currentCell.getColumnIndex(), currentCell.getRowIndex() + 1, templateStr);
varMap.put(name, next);
} else {
// 非列表,一次性填充,即变量填充后,和此单元格去掉关联
- varMap.remove(name);
+ varMap.put(name, null);
}
-
- fill(cell, name, templateStr, rowData);
}
/**
@@ -141,10 +162,11 @@ public class TemplateContext {
*
* @param cell 单元格,非模板中变量所在单元格则为{@link VirtualCell}
* @param name 变量名
- * @param templateStr 模板字符串
- * @param rowData 填充的数据
+ * @param rowDataBean 填充的数据,可以为Map或Bean
+ * @return 是否填充成功,{@code false}表示无数据
*/
- private void fill(Cell cell, final String name, final String templateStr, final Map, ?> rowData) {
+ private boolean fill(Cell cell, final String name, final Object rowDataBean) {
+ final String templateStr = cell.getStringCellValue();
if (cell instanceof VirtualCell) {
// 虚拟单元格,转换为实际单元格
final Cell newCell;
@@ -159,13 +181,23 @@ public class TemplateContext {
final Object cellValue;
// 模板替换
if (StrUtil.equals(name, StrUtil.unWrap(templateStr, VAR_PREFIX, VAR_SUFFIX))) {
- // 一个单元格只有一个变量
- cellValue = rowData.get(name);
+ // 一个单元格只有一个变量,支持多级表达式
+ cellValue = BeanUtil.getProperty(rowDataBean, name);
+ if (null == cellValue) {
+ // 对应表达式无提供的值,跳过填充
+ return false;
+ }
} else {
// 模板中存在多个变量或模板填充,直接赋值为String
- cellValue = StrUtil.format(templateStr, rowData);
+ // 没有找到值的变量保留原样
+ cellValue = StrUtil.formatByBean(templateStr, rowDataBean, false);
+ if (ObjUtil.equals(cellValue, templateStr)) {
+ // 模板无修改,说明没有变量替换,跳过填充
+ return false;
+ }
}
CellUtil.setCellValue(cell, cellValue);
+ return true;
}
/**
diff --git a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/TemplateWriterTest.java b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/TemplateWriterTest.java
index 3d4a0f2c0..a95c32c2a 100644
--- a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/TemplateWriterTest.java
+++ b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/TemplateWriterTest.java
@@ -1,5 +1,6 @@
package org.dromara.hutool.poi.excel.writer;
+import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.poi.excel.ExcelUtil;
import org.junit.jupiter.api.Test;
@@ -28,7 +29,7 @@ public class TemplateWriterTest {
writer.writeRow(createRow(), false);
}
- //writer.flush(FileUtil.file(targetDir + "templateResult.xlsx"), true);
+ writer.flush(FileUtil.file(targetDir + "templateResult.xlsx"), true);
writer.close();
}
@@ -50,7 +51,7 @@ public class TemplateWriterTest {
writer.writeRow(createRow(), false);
}
- //writer.flush(FileUtil.file(targetDir + "templateWithFooterResult.xlsx"), true);
+ writer.flush(FileUtil.file(targetDir + "templateWithFooterResult.xlsx"), true);
writer.close();
}