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 1/4] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=8D=A0=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) + ); + } + +} From 0eb1ecb1b7111d221d1ac4f80d20ed5a811ec447 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 8 Nov 2022 18:09:43 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=99=A8=E9=9D=9E=E7=A9=BA=E6=A0=A1=E9=AA=8C=E6=97=B6=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/text/PlaceholderParser.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 index 50ec2a4f3..c01cd6116 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/PlaceholderParser.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/PlaceholderParser.java @@ -57,10 +57,11 @@ public class PlaceholderParser implements UnaryOperator { * @param close 占位符结束符号,不允许为空 * @param escape 转义符 */ - public PlaceholderParser(UnaryOperator processor, String open, String close, char escape) { + public PlaceholderParser( + final UnaryOperator processor, final String open, final String close, final char escape) { Assert.isFalse(StrChecker.isEmpty(open), "开始符号不能为空"); Assert.isFalse(StrChecker.isEmpty(close), "结束符号不能为空"); - this.processor = processor; + this.processor = Objects.requireNonNull(processor); this.open = open; this.openLength = open.length(); this.close = close; @@ -75,7 +76,8 @@ public class PlaceholderParser implements UnaryOperator { * @param open 占位符开始符号,不允许为空 * @param close 占位符结束符号,不允许为空 */ - public PlaceholderParser(UnaryOperator processor, String open, String close) { + public PlaceholderParser( + final UnaryOperator processor, final String open, final String close) { this(processor, open, close, '\\'); } @@ -86,8 +88,7 @@ public class PlaceholderParser implements UnaryOperator { * @return 处理后的字符串 */ @Override - public String apply(String text) { - Objects.requireNonNull(processor); + public String apply(final String text) { if (StrChecker.isEmpty(text)) { return StrChecker.EMPTY; } From 1d25cbdd243ea2321fd0aaec3a915b51926e1109 Mon Sep 17 00:00:00 2001 From: Zjp <1215582715@qq.com> Date: Wed, 9 Nov 2022 11:12:46 +0800 Subject: [PATCH 3/4] =?UTF-8?q?1.=20=E4=BC=98=E5=8C=96=E4=BA=86=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E7=BB=93=E6=9E=9C=E7=9A=84StringBuilder=E7=9A=84?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E9=95=BF=E5=BA=A6;=202.=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=BA=86expression=E7=9A=84=E9=87=8D=E7=BD=AE?= =?UTF-8?q?=E6=93=8D=E4=BD=9C;=203.=20=E4=BC=98=E5=8C=96=E4=BA=86=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E5=BC=82=E5=B8=B8=E6=97=B6=E7=9A=84=E5=A4=84=E7=90=86?= =?UTF-8?q?;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/text/PlaceholderParser.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 index c01cd6116..2d0aef5b6 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/PlaceholderParser.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/PlaceholderParser.java @@ -1,5 +1,6 @@ package cn.hutool.core.text; +import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.Assert; import java.util.Objects; @@ -102,7 +103,7 @@ public class PlaceholderParser implements UnaryOperator { // 开始匹配 char[] src = text.toCharArray(); - final StringBuilder result = new StringBuilder(); + final StringBuilder result = new StringBuilder(src.length); StringBuilder expression = new StringBuilder(); while (openCursor > -1) { @@ -136,15 +137,14 @@ public class PlaceholderParser implements UnaryOperator { } } - // 未能找到结束符号,说明匹配结束 + // 未能找到结束符号,说明匹配异常 if (end == -1) { - result.append(src, openCursor, src.length - openCursor); - closeCursor = src.length; + throw new UtilException("\"{}\" 中字符下标 {} 处的开始符没有找到对应的结束符", text, openCursor); } // 找到结束符号,将开始到结束符号之间的字符串替换为指定表达式 else { result.append(processor.apply(expression.toString())); - expression = new StringBuilder(); + expression.setLength(0); // 完成当前占位符的处理匹配,寻找下一个 closeCursor = end + close.length(); } From 20d71d0e495fef0fefe19cff250b43eacc09b3c8 Mon Sep 17 00:00:00 2001 From: Zjp <1215582715@qq.com> Date: Wed, 9 Nov 2022 15:24:07 +0800 Subject: [PATCH 4/4] add @author; --- .../src/main/java/cn/hutool/core/text/PlaceholderParser.java | 1 + 1 file changed, 1 insertion(+) 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 index 2d0aef5b6..81c441cb4 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/PlaceholderParser.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/PlaceholderParser.java @@ -17,6 +17,7 @@ import java.util.function.UnaryOperator; * } * * @author huangchengxing + * @author emptypoint */ public class PlaceholderParser implements UnaryOperator {