diff --git a/CHANGELOG.md b/CHANGELOG.md index bf710f0a6..3ced25ba0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ * 【core 】 修复Dict中putAll大小写问题(issue#I1MU5B@Gitee) * 【core 】 修复POI中sax读取数字判断错误问题(issue#931@Github) * 【core 】 修复DateUtil.endOfQuarter错误问题(issue#I1NGZ7@Gitee) +* 【core 】 修复URL中有空格转为+问题(issue#I1NGW4@Gitee) +* 【core 】 修复CollUtil.intersectionDistinct空集合结果错误问题 ------------------------------------------------------------------------------------------------------------- ## 5.3.8 (2020-06-16) diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java index db85adb28..c25987075 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java @@ -283,23 +283,26 @@ public class CollUtil { @SafeVarargs public static Set intersectionDistinct(Collection coll1, Collection coll2, Collection... otherColls) { final Set result; - if (isEmpty(coll1)) { - result = new LinkedHashSet<>(); + if (isEmpty(coll1) || isEmpty(coll2)) { + // 有一个空集合就直接返回空 + return new LinkedHashSet<>(); } else { result = new LinkedHashSet<>(coll1); } - if (isNotEmpty(coll2)) { - result.retainAll(coll2); - } - if (ArrayUtil.isNotEmpty(otherColls)) { for (Collection otherColl : otherColls) { if(isNotEmpty(otherColl)){ result.retainAll(otherColl); + } else { + // 有一个空集合就直接返回空 + return new LinkedHashSet<>(); } } } + + result.retainAll(coll2); + return result; } diff --git a/hutool-core/src/main/java/cn/hutool/core/net/URLEncoder.java b/hutool-core/src/main/java/cn/hutool/core/net/URLEncoder.java index c01365f00..2bfe00ee3 100644 --- a/hutool-core/src/main/java/cn/hutool/core/net/URLEncoder.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/URLEncoder.java @@ -12,24 +12,23 @@ import java.util.BitSet; /** * URL编码,数据内容的类型是 application/x-www-form-urlencoded。 - * + * *
  * 1.字符"a"-"z","A"-"Z","0"-"9",".","-","*",和"_" 都不会被编码;
  * 2.将空格转换为%20 ;
  * 3.将非文本内容转换成"%xy"的形式,xy是两位16进制的数值;
  * 
- * - * @author looly, * + * @author looly */ -public class URLEncoder implements Serializable{ +public class URLEncoder implements Serializable { private static final long serialVersionUID = 1L; // --------------------------------------------------------------------------------------------- Static method start /** * 默认{@link URLEncoder}
* 默认的编码器针对URI路径编码,定义如下: - * + * *
 	 * pchar = unreserved(不处理) / pct-encoded / sub-delims(子分隔符) / ":" / "@"
 	 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
@@ -37,32 +36,42 @@ public class URLEncoder implements Serializable{
 	 * 
*/ public static final URLEncoder DEFAULT = createDefault(); - + /** * 用于查询语句的{@link URLEncoder}
* 编码器针对URI路径编码,定义如下: - * + * *
-	 * 0x20 ' ' =》 '+' 
-	 * 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is 
+	 * 0x20 ' ' =》 '+'
+	 * 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is
 	 * '*', '-', '.', '0' to '9', 'A' to 'Z', '_', 'a' to 'z' Also '=' and '&' 不编码
 	 * 其它编码为 %nn 形式
 	 * 
- * + *

* 详细见:https://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm */ public static final URLEncoder QUERY = createQuery(); + /** + * 全编码的{@link URLEncoder}
+ *

+	 * 	 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is
+	 * 	 '*', '-', '.', '0' to '9', 'A' to 'Z', '_', 'a' to 'z' 不编码
+	 * 	 其它编码为 %nn 形式
+	 * 	 
+ */ + public static final URLEncoder ALL = createAll(); + /** * 创建默认{@link URLEncoder}
* 默认的编码器针对URI路径编码,定义如下: - * + * *
 	 * pchar = unreserved(不处理) / pct-encoded / sub-delims(子分隔符) / ":" / "@"
 	 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
 	 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
 	 * 
- * + * * @return {@link URLEncoder} */ public static URLEncoder createDefault() { @@ -95,16 +104,16 @@ public class URLEncoder implements Serializable{ /** * 创建用于查询语句的{@link URLEncoder}
* 编码器针对URI路径编码,定义如下: - * + * *
-	 * 0x20 ' ' =》 '+' 
-	 * 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is 
+	 * 0x20 ' ' =》 '+'
+	 * 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is
 	 * '*', '-', '.', '0' to '9', 'A' to 'Z', '_', 'a' to 'z' Also '=' and '&' 不编码
 	 * 其它编码为 %nn 形式
 	 * 
- * + *

* 详细见:https://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm - * + * * @return {@link URLEncoder} */ public static URLEncoder createQuery() { @@ -122,16 +131,44 @@ public class URLEncoder implements Serializable{ return encoder; } + + /** + * 创建{@link URLEncoder}
+ * 编码器针对URI路径编码,定义如下: + * + *

+	 * 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is
+	 * '*', '-', '.', '0' to '9', 'A' to 'Z', '_', 'a' to 'z' 不编码
+	 * 其它编码为 %nn 形式
+	 * 
+ *

+ * 详细见:https://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm + * + * @return {@link URLEncoder} + */ + public static URLEncoder createAll() { + final URLEncoder encoder = new URLEncoder(); + encoder.addSafeCharacter('*'); + encoder.addSafeCharacter('-'); + encoder.addSafeCharacter('.'); + encoder.addSafeCharacter('_'); + + return encoder; + } // --------------------------------------------------------------------------------------------- Static method end - /** 存放安全编码 */ + /** + * 存放安全编码 + */ private final BitSet safeCharacters; - /** 是否编码空格为+ */ + /** + * 是否编码空格为+ + */ private boolean encodeSpaceAsPlus = false; /** * 构造
- * + *

* [a-zA-Z0-9]默认不被编码 */ public URLEncoder() { @@ -150,7 +187,7 @@ public class URLEncoder implements Serializable{ /** * 构造 - * + * * @param safeCharacters 安全字符,安全字符不被编码 */ private URLEncoder(BitSet safeCharacters) { @@ -160,7 +197,7 @@ public class URLEncoder implements Serializable{ /** * 增加安全字符
* 安全字符不被编码 - * + * * @param c 字符 */ public void addSafeCharacter(char c) { @@ -170,7 +207,7 @@ public class URLEncoder implements Serializable{ /** * 移除安全字符
* 安全字符不被编码 - * + * * @param c 字符 */ public void removeSafeCharacter(char c) { @@ -179,7 +216,7 @@ public class URLEncoder implements Serializable{ /** * 是否将空格编码为+ - * + * * @param encodeSpaceAsPlus 是否将空格编码为+ */ public void setEncodeSpaceAsPlus(boolean encodeSpaceAsPlus) { @@ -189,9 +226,8 @@ public class URLEncoder implements Serializable{ /** * 将URL中的字符串编码为%形式 * - * @param path 需要编码的字符串 + * @param path 需要编码的字符串 * @param charset 编码 - * * @return 编码后的字符串 */ public String encode(String path, Charset charset) { diff --git a/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java index e2ebfb6b5..fa53d14e2 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java @@ -14,7 +14,6 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.JarURLConnection; import java.net.MalformedURLException; @@ -331,11 +330,8 @@ public class URLUtil { if (null == charset) { return url; } - try { - return java.net.URLEncoder.encode(url, charset.toString()); - } catch (UnsupportedEncodingException e) { - throw new UtilException(e); - } + + return URLEncoder.ALL.encode(url, charset); } /** diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java index 450eee15d..b14391d5c 100644 --- a/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java @@ -27,14 +27,13 @@ import java.util.SortedSet; /** * 集合工具类单元测试 - * - * @author looly * + * @author looly */ public class CollUtilTest { @Test - public void isNotEmptyTest(){ + public void isNotEmptyTest() { Assert.assertFalse(CollUtil.isNotEmpty((Collection) null)); } @@ -79,7 +78,7 @@ public class CollUtilTest { Collection intersection = CollUtil.intersection(list1, list2); Assert.assertEquals(2, CollUtil.count(intersection, t -> t.equals("b"))); } - + @Test public void intersectionDistinctTest() { ArrayList list1 = CollUtil.newArrayList("a", "b", "b", "c", "d", "x"); @@ -87,10 +86,10 @@ public class CollUtilTest { ArrayList list3 = CollUtil.newArrayList(); Collection intersectionDistinct = CollUtil.intersectionDistinct(list1, list2); - Assert.assertEquals(CollUtil.newLinkedHashSet("a", "b", "c", "d"), intersectionDistinct); + Assert.assertEquals(CollUtil.newLinkedHashSet("a", "b", "c", "d"), intersectionDistinct); - Collection intersectionDistinct2 = CollUtil.intersectionDistinct(list1, list2, list3); - Assert.assertTrue(intersectionDistinct2.isEmpty()); + Collection intersectionDistinct2 = CollUtil.intersectionDistinct(list1, list2, list3); + Assert.assertTrue(intersectionDistinct2.isEmpty()); } @Test @@ -144,7 +143,7 @@ public class CollUtilTest { } @Test - public void subtractTest(){ + public void subtractTest() { List list1 = CollUtil.newArrayList("a", "b", "b", "c", "d", "x"); List list2 = CollUtil.newArrayList("a", "b", "b", "b", "c", "d", "x2"); final Collection subtract = CollUtil.subtract(list1, list2); @@ -228,7 +227,7 @@ public class CollUtilTest { Assert.assertSame(list, filtered); Assert.assertEquals(CollUtil.newArrayList("b", "c"), filtered); } - + @Test public void removeNullTest() { ArrayList list = CollUtil.newArrayList("a", "b", "c", null, "", " "); @@ -239,7 +238,7 @@ public class CollUtilTest { Assert.assertSame(list, filtered); Assert.assertEquals(CollUtil.newArrayList("a", "b", "c", "", " "), filtered); } - + @Test public void removeEmptyTest() { ArrayList list = CollUtil.newArrayList("a", "b", "c", null, "", " "); @@ -250,13 +249,13 @@ public class CollUtilTest { Assert.assertSame(list, filtered); Assert.assertEquals(CollUtil.newArrayList("a", "b", "c", " "), filtered); } - + @Test public void removeBlankTest() { ArrayList list = CollUtil.newArrayList("a", "b", "c", null, "", " "); - + ArrayList filtered = CollUtil.removeBlank(list); - + // 原地过滤 Assert.assertSame(list, filtered); Assert.assertEquals(CollUtil.newArrayList("a", "b", "c"), filtered); @@ -632,9 +631,9 @@ public class CollUtilTest { } @Test - public void toMapTest(){ + public void toMapTest() { Collection keys = CollUtil.newArrayList("a", "b", "c", "d"); - final Map map = CollUtil.toMap(keys, new HashMap<>(), (value)->"key" + value); + final Map map = CollUtil.toMap(keys, new HashMap<>(), (value) -> "key" + value); Assert.assertEquals("a", map.get("keya")); Assert.assertEquals("b", map.get("keyb")); Assert.assertEquals("c", map.get("keyc")); diff --git a/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java b/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java index c5f2f1a68..fc861f3fe 100644 --- a/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java @@ -198,4 +198,10 @@ public class UrlBuilderTest { final UrlBuilder builder = UrlBuilder.ofHttp(getWorkDayUrl, CharsetUtil.CHARSET_UTF_8); Assert.assertEquals(getWorkDayUrl, builder.toString()); } + + @Test + public void blankEncodeTest(){ + final UrlBuilder urlBuilder = UrlBuilder.ofHttp("http://a.com/aaa bbb.html", CharsetUtil.CHARSET_UTF_8); + Assert.assertEquals("http://a.com/aaa%20bbb.html", urlBuilder.toString()); + } }