From c4c11ffeb64460dd9ab5f6fbefa8031c06a8459f Mon Sep 17 00:00:00 2001
From: huangchengxing <841396397@qq.com>
Date: Tue, 8 Nov 2022 09:24:05 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=80=E4=B8=AA=E5=8D=A0?=
=?UTF-8?q?=E4=BD=8D=E7=AC=A6=E8=A7=A3=E6=9E=90=E5=99=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../hutool/core/text/PlaceholderParser.java | 162 ++++++++++++++++++
.../core/text/PlaceholderParserTest.java | 44 +++++
2 files changed, 206 insertions(+)
create mode 100644 hutool-core/src/main/java/cn/hutool/core/text/PlaceholderParser.java
create mode 100644 hutool-core/src/test/java/cn/hutool/core/text/PlaceholderParserTest.java
diff --git a/hutool-core/src/main/java/cn/hutool/core/text/PlaceholderParser.java b/hutool-core/src/main/java/cn/hutool/core/text/PlaceholderParser.java
new file mode 100644
index 000000000..50ec2a4f3
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/text/PlaceholderParser.java
@@ -0,0 +1,162 @@
+package cn.hutool.core.text;
+
+import cn.hutool.core.lang.Assert;
+
+import java.util.Objects;
+import java.util.function.UnaryOperator;
+
+/**
+ *
一个简单的占位符解析器。给定占位符的左右边界符号以及转义符,
+ * 将允许把一段字符串中的占位符解析并替换为指定内容,支持指定转义符对边界符号进行转义。
+ * 比如:
+ *
{@code
+ * String text = "select * from #[tableName] where id = #[id]";
+ * PlaceholderParser parser = new PlaceholderParser(str -> "?", "#[", "]");
+ * parser.apply(text); // = select * from ? where id = ?
+ * }
+ *
+ * @author huangchengxing
+ */
+public class PlaceholderParser implements UnaryOperator {
+
+ /**
+ * processor
+ */
+ private final UnaryOperator processor;
+
+ /**
+ * 占位符开始符号
+ */
+ private final String open;
+
+ /**
+ * 结束符号长度
+ */
+ private final int openLength;
+
+ /**
+ * 占位符结束符号
+ */
+ private final String close;
+
+ /**
+ * 结束符号长度
+ */
+ private final int closeLength;
+
+ /**
+ * 转义符
+ */
+ private final char escape;
+
+ /**
+ * 创建一个占位符解析器
+ *
+ * @param processor 占位符处理器
+ * @param open 占位符开始符号,不允许为空
+ * @param close 占位符结束符号,不允许为空
+ * @param escape 转义符
+ */
+ public PlaceholderParser(UnaryOperator processor, String open, String close, char escape) {
+ Assert.isFalse(StrChecker.isEmpty(open), "开始符号不能为空");
+ Assert.isFalse(StrChecker.isEmpty(close), "结束符号不能为空");
+ this.processor = processor;
+ this.open = open;
+ this.openLength = open.length();
+ this.close = close;
+ this.closeLength = close.length();
+ this.escape = escape;
+ }
+
+ /**
+ * 创建一个占位符解析器,默认转义符为{@code "\"}
+ *
+ * @param processor 占位符处理器
+ * @param open 占位符开始符号,不允许为空
+ * @param close 占位符结束符号,不允许为空
+ */
+ public PlaceholderParser(UnaryOperator processor, String open, String close) {
+ this(processor, open, close, '\\');
+ }
+
+ /**
+ * 解析并替换字符串中的占位符
+ *
+ * @param text 待解析的字符串
+ * @return 处理后的字符串
+ */
+ @Override
+ public String apply(String text) {
+ Objects.requireNonNull(processor);
+ if (StrChecker.isEmpty(text)) {
+ return StrChecker.EMPTY;
+ }
+
+ // 寻找第一个开始符号
+ int closeCursor = 0;
+ int openCursor = text.indexOf(open, closeCursor);
+ if (openCursor == -1) {
+ return text;
+ }
+
+ // 开始匹配
+ char[] src = text.toCharArray();
+ final StringBuilder result = new StringBuilder();
+ StringBuilder expression = new StringBuilder();
+ while (openCursor > -1) {
+
+ // 开始符号是否被转义,若是则跳过并寻找下一个开始符号
+ if (openCursor > 0 && src[openCursor - 1] == escape) {
+ result.append(src, closeCursor, openCursor - closeCursor - 1).append(open);
+ closeCursor = openCursor + openLength;
+ openCursor = text.indexOf(open, closeCursor);
+ continue;
+ }
+
+ // 记录当前位符的开始符号与上一占位符的结束符号间的字符串
+ result.append(src, closeCursor, openCursor - closeCursor);
+
+ // 重置结束游标至当前占位符的开始处
+ closeCursor = openCursor + openLength;
+
+ // 寻找结束符号下标
+ int end = text.indexOf(close, closeCursor);
+ while (end > -1) {
+ // 结束符号被转义,寻找下一个结束符号
+ if (end > closeCursor && src[end - 1] == escape) {
+ expression.append(src, closeCursor, end - closeCursor - 1).append(close);
+ closeCursor = end + closeLength;
+ end = text.indexOf(close, closeCursor);
+ }
+ // 找到结束符号
+ else {
+ expression.append(src, closeCursor, end - closeCursor);
+ break;
+ }
+ }
+
+ // 未能找到结束符号,说明匹配结束
+ if (end == -1) {
+ result.append(src, openCursor, src.length - openCursor);
+ closeCursor = src.length;
+ }
+ // 找到结束符号,将开始到结束符号之间的字符串替换为指定表达式
+ else {
+ result.append(processor.apply(expression.toString()));
+ expression = new StringBuilder();
+ // 完成当前占位符的处理匹配,寻找下一个
+ closeCursor = end + close.length();
+ }
+
+ // 寻找下一个开始符号
+ openCursor = text.indexOf(open, closeCursor);
+ }
+
+ // 若匹配结束后仍有未处理的字符串,则直接将其拼接到表达式上
+ if (closeCursor < src.length) {
+ result.append(src, closeCursor, src.length - closeCursor);
+ }
+ return result.toString();
+ }
+
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/text/PlaceholderParserTest.java b/hutool-core/src/test/java/cn/hutool/core/text/PlaceholderParserTest.java
new file mode 100644
index 000000000..bb7b7a3de
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/text/PlaceholderParserTest.java
@@ -0,0 +1,44 @@
+package cn.hutool.core.text;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * test for {@link PlaceholderParser}
+ *
+ * @author huangchengxing
+ */
+public class PlaceholderParserTest {
+
+ @Test
+ public void testParse() {
+ String text = "i {a}{m} a {jvav} programmer";
+ PlaceholderParser parser = new PlaceholderParser(str -> str, "{", "}");
+ Assert.assertEquals(
+ "i am a jvav programmer",
+ parser.apply(text)
+ );
+
+ text = "i [a][m] a [jvav] programmer";
+ parser = new PlaceholderParser(str -> str, "[", "]");
+ Assert.assertEquals(
+ "i am a jvav programmer",
+ parser.apply(text)
+ );
+
+ text = "i \\[a][[m\\]] a [jvav] programmer";
+ parser = new PlaceholderParser(str -> str, "[", "]");
+ Assert.assertEquals(
+ "i [a][m] a jvav programmer",
+ parser.apply(text)
+ );
+
+ text = "i /[a][[m/]] a [jvav] programmer";
+ parser = new PlaceholderParser(str -> str, "[", "]", '/');
+ Assert.assertEquals(
+ "i [a][m] a jvav programmer",
+ parser.apply(text)
+ );
+ }
+
+}