diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/StrTemplate.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/StrTemplate.java index aa777f02e..8e291c80f 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/StrTemplate.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/StrTemplate.java @@ -13,10 +13,7 @@ import org.dromara.hutool.core.text.placeholder.segment.StrTemplateSegment; import org.dromara.hutool.core.text.placeholder.template.NamedPlaceholderStrTemplate; import org.dromara.hutool.core.text.placeholder.template.SinglePlaceholderStrTemplate; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; @@ -276,10 +273,16 @@ public abstract class StrTemplate { } final StringBuilder sb = new StringBuilder(totalTextLength); - final Iterator valueIterator = values.iterator(); + int index = 0; // 构造格式化结果字符串 for (StrTemplateSegment segment : segments) { - segment.format(sb, valueIterator); + if (segment instanceof LiteralSegment) { + sb.append(segment.getText()); + } + // 当前是 占位符,直接 替换为 参数值 + else { + sb.append(values.get(index++)); + } } return sb.toString(); } @@ -596,8 +599,8 @@ public abstract class StrTemplate { *

由于此时子类还没构造完成,所以只能由子类构造方法调用

*/ protected void afterInit() { - // 解析 并 优化 segment 列表 - this.segments = optimizeSegments(parseSegments(template)); + // 释放空闲的列表元素 + this.segments = new ArrayList<>(parseSegments(template)); // 计算 固定文本segment 的 数量 和 文本总长度 int literalSegmentSize = 0, fixedTextTotalLength = 0; @@ -612,9 +615,9 @@ public abstract class StrTemplate { // 获取 占位符segment 列表 final int placeholderSegmentsSize = segments.size() - literalSegmentSize; if (placeholderSegmentsSize == 0) { - this.placeholderSegments = ListUtil.zero(); + this.placeholderSegments = Collections.emptyList(); } else { - List placeholderSegments = new ArrayList<>(placeholderSegmentsSize); + final List placeholderSegments = new ArrayList<>(placeholderSegmentsSize); for (StrTemplateSegment segment : segments) { if (segment instanceof AbstractPlaceholderSegment) { placeholderSegments.add((AbstractPlaceholderSegment) segment); @@ -624,6 +627,26 @@ public abstract class StrTemplate { } } + /** + * 添加 固定文本segment,过滤 空字符串 并 合并相邻的固定文本 + * + * @param isLastLiteralSegment 上一个新增的segment是否是固定文本 + * @param list 已保存的segment列表 + * @param newText 新的固定文本 + */ + protected void addLiteralSegment(boolean isLastLiteralSegment, List list, String newText) { + if (newText.isEmpty()) { + return; + } + if (isLastLiteralSegment) { + // 最后的固定文本segment 和 新固定文本 合并为一个 + int lastIdx = list.size() - 1; + StrTemplateSegment lastLiteralSegment = list.get(lastIdx); + list.set(lastIdx, new LiteralSegment(lastLiteralSegment.getText() + newText)); + } else { + list.add(new LiteralSegment(newText)); + } + } /** * 将 模板 解析为 Segment 列表 @@ -651,45 +674,6 @@ public abstract class StrTemplate { return placeholderSegments; } - /** - * 优化节点列表 - *

移除空文本节点,合并连续的文本节点

- * - * @param segments 节点列表 - * @return 不占用多余空间的节点列表 - */ - private List optimizeSegments(final List segments) { - if (CollUtil.isEmpty(segments)) { - return segments; - } - - final List list = new ArrayList<>(segments.size()); - StrTemplateSegment last; - for (StrTemplateSegment segment : segments) { - if (segment instanceof LiteralSegment) { - // 空的文本节点,没有任何意义 - if (segment.getText().isEmpty()) { - continue; - } - if (list.isEmpty()) { - list.add(segment); - continue; - } - last = list.get(list.size() - 1); - // 如果是两个连续的文本节点,需要合并 - if (last instanceof LiteralSegment) { - list.set(list.size() - 1, new LiteralSegment(last.getText() + segment.getText())); - } else { - list.add(segment); - } - } else { - list.add(segment); - } - } - // 释放空闲的列表元素 - return list.size() == segments.size() ? list : new ArrayList<>(list); - } - /** * 抽象Builder * diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/segment/AbstractPlaceholderSegment.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/segment/AbstractPlaceholderSegment.java index c3dba5c18..3f2b1bbd6 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/segment/AbstractPlaceholderSegment.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/segment/AbstractPlaceholderSegment.java @@ -1,7 +1,5 @@ package org.dromara.hutool.core.text.placeholder.segment; -import java.util.Iterator; - /** * 字符串模板-占位符-抽象 Segment *

例如:{@literal "???"->"???", "{}"->"{}", "{name}"->"name"}

@@ -25,14 +23,6 @@ public abstract class AbstractPlaceholderSegment implements StrTemplateSegment { return placeholder; } - @Override - public void format(final StringBuilder sb, final Iterator valueIterator) { - // 当前是 占位符,直接 替换为 参数值 - if (valueIterator.hasNext()) { - sb.append(valueIterator.next()); - } - } - public String getPlaceholder() { return placeholder; } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/segment/LiteralSegment.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/segment/LiteralSegment.java index 3e2ce0efe..273940552 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/segment/LiteralSegment.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/segment/LiteralSegment.java @@ -1,7 +1,5 @@ package org.dromara.hutool.core.text.placeholder.segment; -import java.util.Iterator; - /** * 字符串模板-固定文本 Segment * @@ -23,10 +21,4 @@ public class LiteralSegment implements StrTemplateSegment { return text; } - @Override - public void format(final StringBuilder sb, final Iterator valueIterator) { - // 在格式化中 拼接 固定文本 - sb.append(text); - } - } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/segment/StrTemplateSegment.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/segment/StrTemplateSegment.java index d69c83e47..f5fc1ee41 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/segment/StrTemplateSegment.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/segment/StrTemplateSegment.java @@ -1,7 +1,5 @@ package org.dromara.hutool.core.text.placeholder.segment; -import java.util.Iterator; - /** * 字符串模板-抽象 Segment * @@ -9,16 +7,6 @@ import java.util.Iterator; * @since 6.0.0 */ public interface StrTemplateSegment { - /** - * 在格式化中,按顺序 拼接 参数值 - * - *

如果是固定文本,则直接拼接,如果是占位符,则拼接参数值

- * - * @param sb 存储格式化结果的变量 - * @param valueIterator 与占位符依次对应的参数值列表 - */ - void format(final StringBuilder sb, final Iterator valueIterator); - /** * 获取文本值 * 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 17bc71071..0e3404e0e 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 @@ -93,6 +93,8 @@ public class NamedPlaceholderStrTemplate extends StrTemplate { String variableName; // 完整的占位符 String wholePlaceholder; + // 上一个解析的segment是否是固定文本,如果是,则需要和当前新的文本部分合并 + boolean isLastLiteralSegment = false; while (openCursor > -1) { // 开始符号是否被转义,若是则跳过,并寻找下一个开始符号 if (openCursor > 0 && src[openCursor - 1] == escape) { @@ -101,9 +103,8 @@ public class NamedPlaceholderStrTemplate extends StrTemplate { hasDoubleEscape = true; } else { // 开始符号被转义,跳过,寻找下一个开始符号 - segments.add(new LiteralSegment( - template.substring(closeCursor, openCursor - 1) + prefix - )); + addLiteralSegment(isLastLiteralSegment, segments, template.substring(closeCursor, openCursor - 1) + prefix); + isLastLiteralSegment = true; closeCursor = openCursor + openLength; openCursor = template.indexOf(prefix, closeCursor); continue; @@ -114,12 +115,12 @@ public class NamedPlaceholderStrTemplate extends StrTemplate { if (!hasDoubleEscape) { if (closeCursor < openCursor) { // 完整记录当前占位符的开始符号与上一占位符的结束符号间的字符串 - segments.add(new LiteralSegment(template.substring(closeCursor, openCursor))); + addLiteralSegment(isLastLiteralSegment, segments, template.substring(closeCursor, openCursor)); } } else { // 存在双转义符,只能保留一个转义符 hasDoubleEscape = false; - segments.add(new LiteralSegment(template.substring(closeCursor, openCursor - 1))); + addLiteralSegment(isLastLiteralSegment, segments, template.substring(closeCursor, openCursor - 1)); } // 重置结束游标至当前占位符的开始处 @@ -166,6 +167,7 @@ public class NamedPlaceholderStrTemplate extends StrTemplate { // 当作变量名称处理 segments.add(new NamedPlaceholderSegment(variableName, wholePlaceholder)); } + isLastLiteralSegment = false; // 完成当前占位符的处理匹配,寻找下一个 closeCursor = end + closeLength; } @@ -176,7 +178,7 @@ public class NamedPlaceholderStrTemplate extends StrTemplate { // 若匹配结束后仍有未处理的字符串,则直接将其拼接到表达式上 if (closeCursor < src.length) { - segments.add(new LiteralSegment(template.substring(closeCursor, src.length))); + addLiteralSegment(isLastLiteralSegment, segments, template.substring(closeCursor)); } return segments; } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/template/SinglePlaceholderStrTemplate.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/template/SinglePlaceholderStrTemplate.java index 5ca295875..224cdeba6 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/template/SinglePlaceholderStrTemplate.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/template/SinglePlaceholderStrTemplate.java @@ -51,6 +51,8 @@ public class SinglePlaceholderStrTemplate extends StrTemplate { int handledPosition = 0; // 占位符所在位置 int delimIndex; + // 上一个解析的segment是否是固定文本,如果是,则需要和当前新的文本部分合并 + boolean lastIsLiteralSegment = false; // 复用的占位符变量 final SinglePlaceholderSegment singlePlaceholderSegment = SinglePlaceholderSegment.newInstance(placeholder); List segments = null; @@ -63,7 +65,7 @@ public class SinglePlaceholderStrTemplate extends StrTemplate { } // 字符串模板剩余部分不再包含占位符 if (handledPosition < strPatternLength) { - segments.add(new LiteralSegment(template.substring(handledPosition, strPatternLength))); + addLiteralSegment(lastIsLiteralSegment, segments, template.substring(handledPosition)); } return segments; } else if (segments == null) { @@ -75,25 +77,25 @@ public class SinglePlaceholderStrTemplate extends StrTemplate { // 存在 双转义符 if (delimIndex > 1 && template.charAt(delimIndex - 2) == escape) { // 转义符之前还有一个转义符,形如:"//{",占位符依旧有效 - segments.add(new LiteralSegment(template.substring(handledPosition, delimIndex - 1))); + addLiteralSegment(lastIsLiteralSegment, segments, template.substring(handledPosition, delimIndex - 1)); segments.add(singlePlaceholderSegment); + lastIsLiteralSegment = false; handledPosition = delimIndex + placeholderLength; } else { // 占位符被转义,形如:"/{",当前字符并不是一个真正的占位符,而是普通字符串的一部分 - segments.add(new LiteralSegment( - template.substring(handledPosition, delimIndex - 1) + placeholder.charAt(0) - )); + addLiteralSegment(lastIsLiteralSegment, segments, template.substring(handledPosition, delimIndex - 1) + placeholder.charAt(0)); + lastIsLiteralSegment = true; handledPosition = delimIndex + 1; } } else { // 正常占位符 - segments.add(new LiteralSegment(template.substring(handledPosition, delimIndex))); + addLiteralSegment(lastIsLiteralSegment, segments, template.substring(handledPosition, delimIndex)); segments.add(singlePlaceholderSegment); + lastIsLiteralSegment = false; handledPosition = delimIndex + placeholderLength; } } } - // region 格式化方法 // ################################################## 格式化方法 ##################################################