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) + ); + } + +}