From 7eb899e22618d15c1a219e620cc358781640a190 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 30 Sep 2022 20:10:11 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DReUtil.replaceAll=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2=E5=8F=98=E9=87=8F=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + .../core/annotation/AnnotationUtil.java | 26 +++---- .../core/comparator/LengthComparator.java | 25 +++++++ .../main/java/cn/hutool/core/util/ReUtil.java | 7 +- .../java/cn/hutool/core/util/ReUtilTest.java | 68 +++++++++++-------- .../main/java/cn/hutool/http/HttpRequest.java | 29 +++++--- 6 files changed, 102 insertions(+), 54 deletions(-) create mode 100755 hutool-core/src/main/java/cn/hutool/core/comparator/LengthComparator.java diff --git a/CHANGELOG.md b/CHANGELOG.md index a3555c9c6..30a624420 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * 【poi 】 修复ExcelReader读取只有标题行报错问题(issue#I5U1JA@Gitee) * 【http 】 修复Http重定向时相对路径导致的问题(issue#I5TPSY@Gitee) * 【http 】 修复Http重定全局设置无效问题(pr#2639@Github) +* 【core 】 修复ReUtil.replaceAll替换变量错误问题(pr#2639@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java index a928b09ef..22070a24e 100755 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java @@ -234,22 +234,22 @@ public class AnnotationUtil { * 获取指定注解属性的值
* 如果无指定的属性方法返回null * - * @param 注解类型 - * @param 注解类型值 - * @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission - * @param propertyName 属性名,例如注解中定义了name()方法,则 此处传入name + * @param 注解类型 + * @param 注解类型值 + * @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission + * @param propertyName 属性名,例如注解中定义了name()方法,则 此处传入name * @return 注解对象 * @throws UtilException 调用注解中的方法时执行异常 * @since 5.8.9 */ - public static R getAnnotationValue(AnnotatedElement annotationEle, Func1 propertyName) { - if(propertyName == null) { + public static R getAnnotationValue(AnnotatedElement annotationEle, Func1 propertyName) { + if (propertyName == null) { return null; - }else { + } else { final SerializedLambda lambda = LambdaUtil.resolve(propertyName); final String instantiatedMethodType = lambda.getInstantiatedMethodType(); - Class annotationClass = ClassUtil.loadClass(StrUtil.sub(instantiatedMethodType, 2, StrUtil.indexOf(instantiatedMethodType, ';'))); - return getAnnotationValue(annotationEle,annotationClass, lambda.getImplMethodName()); + final Class annotationClass = ClassUtil.loadClass(StrUtil.sub(instantiatedMethodType, 2, StrUtil.indexOf(instantiatedMethodType, ';'))); + return getAnnotationValue(annotationEle, annotationClass, lambda.getImplMethodName()); } } @@ -472,10 +472,10 @@ public class AnnotationUtil { public static T getSynthesizedAnnotation(Class annotationType, Annotation... annotations) { // TODO 缓存合成注解信息,避免重复解析 return Opt.ofNullable(annotations) - .filter(ArrayUtil::isNotEmpty) - .map(AnnotationUtil::aggregatingFromAnnotationWithMeta) - .map(a -> a.synthesize(annotationType)) - .get(); + .filter(ArrayUtil::isNotEmpty) + .map(AnnotationUtil::aggregatingFromAnnotationWithMeta) + .map(a -> a.synthesize(annotationType)) + .get(); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/comparator/LengthComparator.java b/hutool-core/src/main/java/cn/hutool/core/comparator/LengthComparator.java new file mode 100755 index 000000000..0c8f0ea3d --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/comparator/LengthComparator.java @@ -0,0 +1,25 @@ +package cn.hutool.core.comparator; + +import java.util.Comparator; + +/** + * 字符串长度比较器,短在前 + * + * @author looly + * @since 5.8.9 + */ +public class LengthComparator implements Comparator { + /** + * 单例的字符串长度比较器,短在前 + */ + public static final LengthComparator INSTANCE = new LengthComparator(); + + @Override + public int compare(CharSequence o1, CharSequence o2) { + int result = Integer.compare(o1.length(), o2.length()); + if (0 == result) { + result = CompareUtil.compare(o1.toString(), o2.toString()); + } + return result; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java index 9fbe76341..11398b7f9 100755 --- a/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ReUtil.java @@ -1,6 +1,7 @@ package cn.hutool.core.util; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.comparator.LengthComparator; import cn.hutool.core.convert.Convert; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.Assert; @@ -878,12 +879,12 @@ public class ReUtil { final Matcher matcher = pattern.matcher(content); boolean result = matcher.find(); if (result) { - final Set varNums = findAll(PatternPool.GROUP_VAR, replacementTemplate, 1, new HashSet<>()); + final Set varNums = findAll(PatternPool.GROUP_VAR, replacementTemplate, 1, new TreeSet<>(LengthComparator.INSTANCE.reversed())); final StringBuffer sb = new StringBuffer(); do { String replacement = replacementTemplate; - for (String var : varNums) { - int group = Integer.parseInt(var); + for (final String var : varNums) { + final int group = Integer.parseInt(var); replacement = replacement.replace("$" + var, matcher.group(group)); } matcher.appendReplacement(sb, escape(replacement)); diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ReUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ReUtilTest.java index ee2d51cbb..f9e4c5efe 100755 --- a/hutool-core/src/test/java/cn/hutool/core/util/ReUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ReUtilTest.java @@ -16,36 +16,36 @@ public class ReUtilTest { @Test public void getTest() { - String resultGet = ReUtil.get("\\w{2}", content, 0); + final String resultGet = ReUtil.get("\\w{2}", content, 0); Assert.assertEquals("ZZ", resultGet); } @Test public void extractMultiTest() { // 抽取多个分组然后把它们拼接起来 - String resultExtractMulti = ReUtil.extractMulti("(\\w)aa(\\w)", content, "$1-$2"); + final String resultExtractMulti = ReUtil.extractMulti("(\\w)aa(\\w)", content, "$1-$2"); Assert.assertEquals("Z-a", resultExtractMulti); } @Test public void extractMultiTest2() { // 抽取多个分组然后把它们拼接起来 - String resultExtractMulti = ReUtil.extractMulti("(\\w)(\\w)(\\w)(\\w)(\\w)(\\w)(\\w)(\\w)(\\w)(\\w)", content, "$1-$2-$3-$4-$5-$6-$7-$8-$9-$10"); + final String resultExtractMulti = ReUtil.extractMulti("(\\w)(\\w)(\\w)(\\w)(\\w)(\\w)(\\w)(\\w)(\\w)(\\w)", content, "$1-$2-$3-$4-$5-$6-$7-$8-$9-$10"); Assert.assertEquals("Z-Z-Z-a-a-a-b-b-b-c", resultExtractMulti); } @Test public void delFirstTest() { // 删除第一个匹配到的内容 - String resultDelFirst = ReUtil.delFirst("(\\w)aa(\\w)", content); + final String resultDelFirst = ReUtil.delFirst("(\\w)aa(\\w)", content); Assert.assertEquals("ZZbbbccc中文1234", resultDelFirst); } @Test public void delLastTest(){ - String blank = ""; - String word = "180公斤"; - String sentence = "10.商品KLS100021型号xxl适合身高180体重130斤的用户"; + final String blank = ""; + final String word = "180公斤"; + final String sentence = "10.商品KLS100021型号xxl适合身高180体重130斤的用户"; //空字符串兼容 Assert.assertEquals(blank,ReUtil.delLast("\\d+", blank)); Assert.assertEquals(blank,ReUtil.delLast(PatternPool.NUMBERS, blank)); @@ -71,30 +71,30 @@ public class ReUtilTest { @Test public void delAllTest() { // 删除所有匹配到的内容 - String content = "发东方大厦eee![images]http://abc.com/2.gpg]好机会eee![images]http://abc.com/2.gpg]好机会"; - String resultDelAll = ReUtil.delAll("!\\[images\\][^\\u4e00-\\u9fa5\\\\s]*", content); + final String content = "发东方大厦eee![images]http://abc.com/2.gpg]好机会eee![images]http://abc.com/2.gpg]好机会"; + final String resultDelAll = ReUtil.delAll("!\\[images\\][^\\u4e00-\\u9fa5\\\\s]*", content); Assert.assertEquals("发东方大厦eee好机会eee好机会", resultDelAll); } @Test public void findAllTest() { // 查找所有匹配文本 - List resultFindAll = ReUtil.findAll("\\w{2}", content, 0, new ArrayList<>()); - ArrayList expected = CollectionUtil.newArrayList("ZZ", "Za", "aa", "bb", "bc", "cc", "12", "34"); + final List resultFindAll = ReUtil.findAll("\\w{2}", content, 0, new ArrayList<>()); + final ArrayList expected = CollectionUtil.newArrayList("ZZ", "Za", "aa", "bb", "bc", "cc", "12", "34"); Assert.assertEquals(expected, resultFindAll); } @Test public void getFirstNumberTest() { // 找到匹配的第一个数字 - Integer resultGetFirstNumber = ReUtil.getFirstNumber(content); + final Integer resultGetFirstNumber = ReUtil.getFirstNumber(content); Assert.assertEquals(Integer.valueOf(1234), resultGetFirstNumber); } @Test public void isMatchTest() { // 给定字符串是否匹配给定正则 - boolean isMatch = ReUtil.isMatch("\\w+[\u4E00-\u9FFF]+\\d+", content); + final boolean isMatch = ReUtil.isMatch("\\w+[\u4E00-\u9FFF]+\\d+", content); Assert.assertTrue(isMatch); } @@ -102,20 +102,20 @@ public class ReUtilTest { public void replaceAllTest() { //通过正则查找到字符串,然后把匹配到的字符串加入到replacementTemplate中,$1表示分组1的字符串 //此处把1234替换为 ->1234<- - String replaceAll = ReUtil.replaceAll(content, "(\\d+)", "->$1<-"); + final String replaceAll = ReUtil.replaceAll(content, "(\\d+)", "->$1<-"); Assert.assertEquals("ZZZaaabbbccc中文->1234<-", replaceAll); } @Test public void replaceAllTest2() { //此处把1234替换为 ->1234<- - String replaceAll = ReUtil.replaceAll(this.content, "(\\d+)", parameters -> "->" + parameters.group(1) + "<-"); + final String replaceAll = ReUtil.replaceAll(this.content, "(\\d+)", parameters -> "->" + parameters.group(1) + "<-"); Assert.assertEquals("ZZZaaabbbccc中文->1234<-", replaceAll); } @Test public void replaceTest() { - String str = "AAABBCCCBBDDDBB"; + final String str = "AAABBCCCBBDDDBB"; String replace = StrUtil.replace(str, 0, "BB", "22", false); Assert.assertEquals("AAA22CCC22DDD22", replace); @@ -138,22 +138,22 @@ public class ReUtilTest { @Test public void escapeTest() { //转义给定字符串,为正则相关的特殊符号转义 - String escape = ReUtil.escape("我有个$符号{}"); + final String escape = ReUtil.escape("我有个$符号{}"); Assert.assertEquals("我有个\\$符号\\{\\}", escape); } @Test public void escapeTest2(){ - String str = "a[bbbc"; - String re = "["; + final String str = "a[bbbc"; + final String re = "["; final String s = ReUtil.get(ReUtil.escape(re), str, 0); Assert.assertEquals("[", s); } @Test public void escapeTest3(){ - String context = "{prefix}_"; - String regex = "{prefix}_"; + final String context = "{prefix}_"; + final String regex = "{prefix}_"; final boolean b = ReUtil.isMatch(ReUtil.escape(regex), context); Assert.assertTrue(b); } @@ -161,7 +161,7 @@ public class ReUtilTest { @Test public void getAllGroupsTest() { //转义给定字符串,为正则相关的特殊符号转义 - Pattern pattern = Pattern.compile("(\\d+)-(\\d+)-(\\d+)"); + final Pattern pattern = Pattern.compile("(\\d+)-(\\d+)-(\\d+)"); List allGroups = ReUtil.getAllGroups(pattern, "192-168-1-1"); Assert.assertEquals("192-168-1", allGroups.get(0)); Assert.assertEquals("192", allGroups.get(1)); @@ -183,23 +183,31 @@ public class ReUtilTest { @Test public void getByGroupNameTest() { - String content = "2021-10-11"; - String regex = "(?\\d+)-(?\\d+)-(?\\d+)"; - String year = ReUtil.get(regex, content, "year"); + final String content = "2021-10-11"; + final String regex = "(?\\d+)-(?\\d+)-(?\\d+)"; + final String year = ReUtil.get(regex, content, "year"); Assert.assertEquals("2021", year); - String month = ReUtil.get(regex, content, "month"); + final String month = ReUtil.get(regex, content, "month"); Assert.assertEquals("10", month); - String day = ReUtil.get(regex, content, "day"); + final String day = ReUtil.get(regex, content, "day"); Assert.assertEquals("11", day); } @Test public void getAllGroupNamesTest() { - String content = "2021-10-11"; - String regex = "(?\\d+)-(?\\d+)-(?\\d+)"; - Map map = ReUtil.getAllGroupNames(PatternPool.get(regex, Pattern.DOTALL), content); + final String content = "2021-10-11"; + final String regex = "(?\\d+)-(?\\d+)-(?\\d+)"; + final Map map = ReUtil.getAllGroupNames(PatternPool.get(regex, Pattern.DOTALL), content); Assert.assertEquals(map.get("year"), "2021"); Assert.assertEquals(map.get("month"), "10"); Assert.assertEquals(map.get("day"), "11"); } + + @Test + public void issuesI5TQDRTest(){ + final Pattern patternIp = Pattern.compile("((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})\\.((2(5[0-5]|[0-4]\\d))" + + "|[0-1]?\\d{1,2})\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})"); + final String s = ReUtil.replaceAll("1.2.3.4", patternIp, "$1.**.**.$10"); + Assert.assertEquals("1.**.**.4", s); + } } diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java index 82ae49e01..ef9694c14 100755 --- a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java +++ b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java @@ -828,13 +828,26 @@ public class HttpRequest extends HttpBase { * 设置是否打开重定向,如果打开默认重定向次数为2
* 此方法效果与{@link #setMaxRedirectCount(int)} 一致 * + *

+ * 需要注意的是,当设置为{@code true}时,如果全局重定向次数非0,直接复用,否则设置默认2次。
+ * 当设置为{@code false}时,无论全局是否设置次数,都设置为0。
+ * 不调用此方法的情况下,使用全局默认的次数。 + *

+ * * @param isFollowRedirects 是否打开重定向 * @return this */ public HttpRequest setFollowRedirects(boolean isFollowRedirects) { - if(isFollowRedirects && config.maxRedirectCount <= 0){ - // 默认两次跳转 - return setMaxRedirectCount(2); + if (isFollowRedirects) { + if (config.maxRedirectCount <= 0) { + // 默认两次跳转 + return setMaxRedirectCount(2); + } + } else { + // 手动强制关闭重定向,此时不受全局重定向设置影响 + if (config.maxRedirectCount < 0) { + return setMaxRedirectCount(0); + } } return this; } @@ -1034,10 +1047,10 @@ public class HttpRequest extends HttpBase { * 执行Request请求后,对响应内容后续处理
* 处理结束后关闭连接 * - * @param 处理结果类型 + * @param 处理结果类型 * @param function 响应内容处理函数 - * @since 5.8.5 * @return 处理结果 + * @since 5.8.5 */ public T thenFunction(Function function) { try (final HttpResponse response = execute(true)) { @@ -1242,15 +1255,15 @@ public class HttpRequest extends HttpBase { if (HttpStatus.isRedirected(responseCode)) { final UrlBuilder redirectUrl; String location = httpConnection.header(Header.LOCATION); - if(false == HttpUtil.isHttp(location) && false == HttpUtil.isHttps(location)){ + if (false == HttpUtil.isHttp(location) && false == HttpUtil.isHttps(location)) { // issue#I5TPSY // location可能为相对路径 - if(false == location.startsWith("/")){ + if (false == location.startsWith("/")) { location = StrUtil.addSuffixIfNot(this.url.getPathStr(), "/") + location; } redirectUrl = UrlBuilder.of(this.url.getScheme(), this.url.getHost(), this.url.getPort() , location, null, null, this.charset); - } else{ + } else { redirectUrl = UrlBuilder.ofHttpWithoutEncode(location); } setUrl(redirectUrl);