Merge remote-tracking branch 'origin/v5-dev' into v5-dev

This commit is contained in:
duandazhi 2021-03-28 19:51:36 +08:00
commit 015d6ae9f3
54 changed files with 541 additions and 206 deletions

View File

@ -3,14 +3,29 @@
-------------------------------------------------------------------------------------------------------------
# 5.6.2 (2021-03-21)
# 5.6.3 (2021-03-28)
### 新特性
### Bug修复
-------------------------------------------------------------------------------------------------------------
# 5.6.2 (2021-03-28)
### 新特性
* 【core 】 Validator增加车架号(车辆识别码)验证、驾驶证驾驶证档案编号的正则校验pr#280@Gitee
* 【core 】 CopyOptions增加propertiesFilterpr#281@Gitee
* 【extra 】 增加Wit模板引擎支持
* 【core 】 增加DesensitizedUtilpr#282@Gitee
* 【core 】 增加DateTime字符串构造issue#I3CQZG@Gitee
* 【core 】 修改ArrayUtil代码风格pr#287@Gitee
* 【json 】 JSONConfig增加setStripTrailingZeros配置issue#I3DJI8@Gitee
* 【db 】 升级兼容BeeCP3.x
### Bug修复
* 【core 】 修复FileTypeUtil中OFD格式判断问题pr#1489@Github
* 【core 】 修复CamelCaseLinkedMap和CaseInsensitiveLinkedMap的Linked失效问题pr#1490@Github
* 【core 】 修复UrlPath中=被转义的问题
-------------------------------------------------------------------------------------------------------------

View File

@ -125,19 +125,19 @@ Each module can be introduced individually, or all modules can be introduced by
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.6.2</version>
<version>5.6.3</version>
</dependency>
```
### Gradle
```
compile 'cn.hutool:hutool-all:5.6.2'
compile 'cn.hutool:hutool-all:5.6.3'
```
## Download
- [Maven1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.6.2/)
- [Maven2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.6.2/)
- [Maven1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.6.3/)
- [Maven2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.6.3/)
> note:
> Hutool 5.x supports JDK8+ and is not tested on Android platforms, and cannot guarantee that all tool classes or tool methods are available.

View File

@ -123,20 +123,20 @@ Hutool的存在就是为了减少代码搜索成本避免网络上参差不
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.6.2</version>
<version>5.6.3</version>
</dependency>
```
### Gradle
```
compile 'cn.hutool:hutool-all:5.6.2'
compile 'cn.hutool:hutool-all:5.6.3'
```
### 非Maven项目
点击以下任一链接,下载`hutool-all-X.X.X.jar`即可:
- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.6.2/)
- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.6.3/)
> 注意
> Hutool 5.x支持JDK8+对Android平台没有测试不能保证所有工具类或工具方法可用。

View File

@ -1 +1 @@
5.6.2
5.6.3

View File

@ -1 +1 @@
var version = '5.6.2'
var version = '5.6.3'

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-all</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-aop</artifactId>
@ -19,7 +19,7 @@
<properties>
<!-- versions -->
<cglib.version>3.3.0</cglib.version>
<spring.version>5.3.4</spring.version>
<spring.version>5.3.5</spring.version>
</properties>
<dependencies>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-bloomFilter</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-bom</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-cache</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-captcha</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-core</artifactId>

View File

@ -122,7 +122,7 @@ public class DateTime extends Date {
*/
public DateTime(Date date) {
this(
date.getTime(),//
date,//
(date instanceof DateTime) ? ((DateTime) date).timeZone : TimeZone.getDefault()
);
}
@ -135,7 +135,7 @@ public class DateTime extends Date {
* @since 4.1.2
*/
public DateTime(Date date, TimeZone timeZone) {
this(date.getTime(), timeZone);
this(ObjectUtil.defaultIfNull(date, new Date()).getTime(), timeZone);
}
/**
@ -211,6 +211,38 @@ public class DateTime extends Date {
this.timeZone = ObjectUtil.defaultIfNull(timeZone, TimeZone.getDefault());
}
/**
* 构造格式<br>
* <ol>
* <li>yyyy-MM-dd HH:mm:ss</li>
* <li>yyyy/MM/dd HH:mm:ss</li>
* <li>yyyy.MM.dd HH:mm:ss</li>
* <li>yyyy年MM月dd日 HH时mm分ss秒</li>
* <li>yyyy-MM-dd</li>
* <li>yyyy/MM/dd</li>
* <li>yyyy.MM.dd</li>
* <li>HH:mm:ss</li>
* <li>HH时mm分ss秒</li>
* <li>yyyy-MM-dd HH:mm</li>
* <li>yyyy-MM-dd HH:mm:ss.SSS</li>
* <li>yyyyMMddHHmmss</li>
* <li>yyyyMMddHHmmssSSS</li>
* <li>yyyyMMdd</li>
* <li>EEE, dd MMM yyyy HH:mm:ss z</li>
* <li>EEE MMM dd HH:mm:ss zzz yyyy</li>
* <li>yyyy-MM-dd'T'HH:mm:ss'Z'</li>
* <li>yyyy-MM-dd'T'HH:mm:ss.SSS'Z'</li>
* <li>yyyy-MM-dd'T'HH:mm:ssZ</li>
* <li>yyyy-MM-dd'T'HH:mm:ss.SSSZ</li>
* </ol>
*
* @param dateStr Date字符串
* @since 5.6.2
*/
public DateTime(CharSequence dateStr) {
this(DateUtil.parse(dateStr));
}
/**
* 构造
*

View File

@ -60,7 +60,6 @@ public class FileTypeUtil {
FILE_TYPE_MAP.put("4d546864000000060001", "mid"); // MIDI (mid)
FILE_TYPE_MAP.put("526172211a0700cf9073", "rar"); // WinRAR
FILE_TYPE_MAP.put("235468697320636f6e66", "ini");
FILE_TYPE_MAP.put("504B0304140000000800", "ofd"); // ofd文件 国标版式文件
FILE_TYPE_MAP.put("504B03040a0000000000", "jar");
FILE_TYPE_MAP.put("504B0304140008000800", "jar");
// MS Excel 注意wordmsi excel的文件头一样
@ -140,7 +139,7 @@ public class FileTypeUtil {
* <pre>
* 1无法识别类型默认按照扩展名识别
* 2xlsdocmsi头信息无法区分按照扩展名区分
* 3zip可能为docxxlsxpptxjarwar头信息无法区分按照扩展名区分
* 3zip可能为docxxlsxpptxjarwarofd头信息无法区分按照扩展名区分
* </pre>
* @param in {@link InputStream}
* @param filename 文件名
@ -162,7 +161,7 @@ public class FileTypeUtil {
typeName = "msi";
}
} else if ("zip".equals(typeName)) {
// zip可能为docxxlsxpptxjarwar等格式扩展名辅助判断
// zip可能为docxxlsxpptxjarwarofd等格式扩展名辅助判断
final String extName = FileUtil.extName(filename);
if ("docx".equalsIgnoreCase(extName)) {
typeName = "docx";
@ -174,6 +173,8 @@ public class FileTypeUtil {
typeName = "jar";
} else if ("war".equalsIgnoreCase(extName)) {
typeName = "war";
} else if ("ofd".equalsIgnoreCase(extName)) {
typeName = "ofd";
}
}
return typeName;

View File

@ -1,5 +1,7 @@
package cn.hutool.core.map;
import cn.hutool.core.util.StrUtil;
import java.util.LinkedHashMap;
import java.util.Map;
@ -13,7 +15,7 @@ import java.util.Map;
* @param <V> 值类型
* @since 4.0.7
*/
public class CamelCaseLinkedMap<K, V> extends CamelCaseMap<K, V> {
public class CamelCaseLinkedMap<K, V> extends CustomKeyMap<K, V> {
private static final long serialVersionUID = 4043263744224569870L;
// ------------------------------------------------------------------------- Constructor start
@ -63,4 +65,18 @@ public class CamelCaseLinkedMap<K, V> extends CamelCaseMap<K, V> {
super(new LinkedHashMap<>(initialCapacity, loadFactor));
}
// ------------------------------------------------------------------------- Constructor end
/**
* 将Key转为驼峰风格如果key为字符串的话
*
* @param key KEY
* @return 驼峰Key
*/
@Override
protected Object customKey(Object key) {
if (key instanceof CharSequence) {
key = StrUtil.toCamelCase(key.toString());
}
return key;
}
}

View File

@ -13,7 +13,7 @@ import java.util.Map;
* @param <V> 值类型
* @since 3.3.1
*/
public class CaseInsensitiveLinkedMap<K, V> extends CaseInsensitiveMap<K, V> {
public class CaseInsensitiveLinkedMap<K, V> extends CustomKeyMap<K, V> {
private static final long serialVersionUID = 4043263744224569870L;
// ------------------------------------------------------------------------- Constructor start
@ -64,4 +64,18 @@ public class CaseInsensitiveLinkedMap<K, V> extends CaseInsensitiveMap<K, V> {
super(new LinkedHashMap<>(initialCapacity, loadFactor));
}
// ------------------------------------------------------------------------- Constructor end
/**
* 将Key转为小写
*
* @param key KEY
* @return 小写KEY
*/
@Override
protected Object customKey(Object key) {
if (key instanceof CharSequence) {
key = key.toString().toLowerCase();
}
return key;
}
}

View File

@ -128,7 +128,7 @@ public class UrlPath {
final StringBuilder builder = new StringBuilder();
for (String segment : segments) {
builder.append(CharUtil.SLASH).append(URLUtil.encodeAll(segment, charset));
builder.append(CharUtil.SLASH).append(URLUtil.encode(segment, charset));
}
if (withEngTag || StrUtil.isEmpty(builder)) {
builder.append(CharUtil.SLASH);

View File

@ -54,6 +54,30 @@ public class UnicodeUtil {
return sb.toString();
}
/**
* 字符编码为Unicode形式
*
* @param c 被编码的字符
* @return Unicode字符串
* @since 5.6.2
* @see HexUtil#toUnicodeHex(char)
*/
public static String toUnicode(char c) {
return HexUtil.toUnicodeHex(c);
}
/**
* 字符编码为Unicode形式
*
* @param c 被编码的字符
* @return Unicode字符串
* @since 5.6.2
* @see HexUtil#toUnicodeHex(int)
*/
public static String toUnicode(int c) {
return HexUtil.toUnicodeHex(c);
}
/**
* 字符串编码为Unicode形式
*

View File

@ -89,7 +89,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
* @return 是否为非空
*/
public static <T> boolean isNotEmpty(T[] array) {
return (array != null && array.length != 0);
return (null != array && array.length != 0);
}
/**
@ -653,7 +653,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
* @since 3.2.2
*/
public static <T extends CharSequence> T[] removeEmpty(T[] array) {
return filter(array, (Filter<T>) t -> false == StrUtil.isEmpty(t));
return filter(array, StrUtil::isNotEmpty);
}
/**
@ -665,7 +665,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
* @since 3.2.2
*/
public static <T extends CharSequence> T[] removeBlank(T[] array) {
return filter(array, (Filter<T>) t -> false == StrUtil.isBlank(t));
return filter(array, StrUtil::isNotBlank);
}
/**
@ -900,11 +900,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
* @return 是否为数组对象如果为{@code null} 返回false
*/
public static boolean isArray(Object obj) {
if (null == obj) {
// throw new NullPointerException("Object check for isArray is null");
return false;
}
return obj.getClass().isArray();
return null != obj && obj.getClass().isArray();
}
/**
@ -1227,7 +1223,13 @@ public class ArrayUtil extends PrimitiveArrayUtil {
* @return 连接后的字符串
*/
public static String join(Object array, CharSequence conjunction) {
if (isArray(array)) {
if(null == array){
throw new NullPointerException("Array must be not null!");
}
if (false == isArray(array)) {
throw new IllegalArgumentException(StrUtil.format("[{}] is not a Array!", array.getClass()));
}
final Class<?> componentType = array.getClass().getComponentType();
if (componentType.isPrimitive()) {
final String componentTypeName = componentType.getName();
@ -1255,8 +1257,6 @@ public class ArrayUtil extends PrimitiveArrayUtil {
return join((Object[]) array, conjunction);
}
}
throw new UtilException(StrUtil.format("[{}] is not a Array!", array.getClass()));
}
/**
* {@link ByteBuffer} 转byte数组
@ -1266,7 +1266,9 @@ public class ArrayUtil extends PrimitiveArrayUtil {
* @since 3.0.1
*/
public static byte[] toArray(ByteBuffer bytebuffer) {
if (false == bytebuffer.hasArray()) {
if (bytebuffer.hasArray()) {
return Arrays.copyOfRange(bytebuffer.array(), bytebuffer.position(), bytebuffer.limit());
} else {
int oldPosition = bytebuffer.position();
bytebuffer.position(0);
int size = bytebuffer.limit();
@ -1274,8 +1276,6 @@ public class ArrayUtil extends PrimitiveArrayUtil {
bytebuffer.get(buffers);
bytebuffer.position(oldPosition);
return buffers;
} else {
return Arrays.copyOfRange(bytebuffer.array(), bytebuffer.position(), bytebuffer.limit());
}
}

View File

@ -366,4 +366,47 @@ public class CharUtil {
public static int digit16(int b) {
return Character.digit(b, 16);
}
/**
* 将字母数字转换为带圈的字符
* <pre>
* '1' - '①'
* 'A' - 'Ⓐ'
* 'a' - 'ⓐ'
* </pre>
*
* @param c 被转换的字符如果字符不支持转换返回原字符
* @return 转换后的字符
* @since 5.6.2
*/
public static char toCloseChar(char c){
int result = c;
if(c >='1' && c <= '9'){
result = '①' + c - '1';
} else if(c >='A' && c <= 'Z'){
result = 'Ⓐ' + c - 'A';
} else if(c >='a' && c <= 'z'){
result = 'ⓐ' + c - 'a';
}
return (char) result;
}
/**
* [1-20]数字转换为带圈的字符
* <pre>
* 1 - '①'
* 12 - '⑫'
* 20 - '⑳'
* </pre>
*
* @param number 被转换的数字
* @return 转换后的字符
* @since 5.6.2
*/
public static char toCloseByNumber(int number){
if(number > 20){
throw new IllegalArgumentException("Number must be [1-20]");
}
return (char) ('①' + number - 1);
}
}

View File

@ -57,11 +57,12 @@ public class DesensitizedUtil {
* DesensitizedUtil.desensitized("duandazhi-jack@gmail.com.cn", DesensitizedUtils.DesensitizedType.EMAIL)) = "d*************@gmail.com.cn"
* DesensitizedUtil.desensitized("1234567890", DesensitizedUtils.DesensitizedType.PASSWORD)) = "**********"
* DesensitizedUtil.desensitized("苏D40000", DesensitizedUtils.DesensitizedType.CAR_LICENSE)) = "苏D4***0"
* </pre>
*
* @author dazer and neusoft and qiaomu
* @param str 字符串
* @param desensitizedType 脱敏类型;可以脱敏用户id中文名身份证号座机号手机号地址电子邮件密码
* @return 脱敏之后的字符串
* @author dazer and neusoft and qiaomu
* @since 5.6.2
*/
public static String desensitized(CharSequence str, DesensitizedUtil.DesensitizedType desensitizedType) {
@ -191,7 +192,7 @@ public class DesensitizedUtil {
}
/**
* 电子邮箱邮箱前缀仅显示第一个字母前缀其他隐藏用星号代替@及后面的地址显示比如d**@126.com>
* 电子邮箱邮箱前缀仅显示第一个字母前缀其他隐藏用星号代替@及后面的地址显示比如d**@126.com
*
* @param email 邮箱
* @return 脱敏后的邮箱
@ -222,11 +223,11 @@ public class DesensitizedUtil {
/**
* 中国车牌车牌中间用*代替
* eg1null => ""
* eg1"" => ""
* eg3苏D40000 => 苏D4***0
* eg4陕A12345D => 陕A1****D
* eg5京A123 => 京A123 如果是错误的车牌不处理
* eg1null - ""
* eg1"" - ""
* eg3苏D40000 - 苏D4***0
* eg4陕A12345D - 陕A1****D
* eg5京A123 - 京A123 如果是错误的车牌不处理
*
* @param carLicense 完整的车牌号
* @return 脱敏后的车牌

View File

@ -262,7 +262,7 @@ public class HexUtil {
* 转换的字符串如果u后不足4位则前面用0填充例如
*
* <pre>
* '' =\u4f60
* '' =\u4f60
* </pre>
*
* @param value int值也可以是char
@ -287,7 +287,7 @@ public class HexUtil {
* 转换的字符串如果u后不足4位则前面用0填充例如
*
* <pre>
* '我' =\u4f60
* '你' ='\u4f60'
* </pre>
*
* @param ch char值

View File

@ -1,6 +1,7 @@
package cn.hutool.core.collection;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.lang.Editor;
import cn.hutool.core.lang.Filter;

View File

@ -32,6 +32,27 @@ public class DateTimeTest {
Assert.assertEquals(5, day);
}
@Test
public void datetimeTest2() {
DateTime dateTime = new DateTime("2017-01-05 12:34:23");
//
int year = dateTime.year();
Assert.assertEquals(2017, year);
// 季度非季节
Quarter season = dateTime.quarterEnum();
Assert.assertEquals(Quarter.Q1, season);
// 月份
Month month = dateTime.monthEnum();
Assert.assertEquals(Month.JANUARY, month);
//
int day = dateTime.dayOfMonth();
Assert.assertEquals(5, day);
}
@Test
public void quarterTest() {
DateTime dateTime = new DateTime("2017-01-05 12:34:23", DatePattern.NORM_DATETIME_FORMAT);

View File

@ -235,4 +235,12 @@ public class UrlBuilderTest {
final UrlBuilder urlBuilder = UrlBuilder.of(webUrl, StandardCharsets.UTF_8);
Assert.assertEquals("a=123&b=4%3F6&c=789", urlBuilder.getQueryStr());
}
@Test
public void encodePathTest(){
// Path中的某些符号无需转义比如=
final String urlStr = "http://hq.sinajs.cn/list=sh600519";
final UrlBuilder urlBuilder = UrlBuilder.ofHttp(urlStr, CharsetUtil.CHARSET_UTF_8);
Assert.assertEquals(urlStr, urlBuilder.toString());
}
}

View File

@ -53,6 +53,12 @@ public class ArrayUtilTest {
public void isNotEmptyTest() {
int[] a = {1, 2};
Assert.assertTrue(ArrayUtil.isNotEmpty(a));
String[] b = {"a", "b", "c"};
Assert.assertTrue(ArrayUtil.isNotEmpty(b));
Object c = new Object[]{"1", "2", 3, 4D};
Assert.assertTrue(ArrayUtil.isNotEmpty(c));
}
@Test
@ -253,15 +259,19 @@ public class ArrayUtilTest {
String[] array = {"aa", "bb", "cc", "dd"};
String join = ArrayUtil.join(array, ",", "[", "]");
Assert.assertEquals("[aa],[bb],[cc],[dd]", join);
Object array2 = new String[]{"aa", "bb", "cc", "dd"};
String join2 = ArrayUtil.join(array2, ",");
Assert.assertEquals("aa,bb,cc,dd", join2);
}
@Test
public void getArrayTypeTest() {
Class<?> arrayType = ArrayUtil.getArrayType(int.class);
Assert.assertEquals(int[].class, arrayType);
Assert.assertSame(int[].class, arrayType);
arrayType = ArrayUtil.getArrayType(String.class);
Assert.assertEquals(String[].class, arrayType);
Assert.assertSame(String[].class, arrayType);
}
@Test
@ -384,4 +394,34 @@ public class ArrayUtilTest {
final int[] reverse = ArrayUtil.reverse(a);
Assert.assertArrayEquals(new int[]{4,3,2,1}, reverse);
}
@Test
public void removeEmptyTest() {
String[] a = {"a", "b", "", null, " ", "c"};
String[] resultA = {"a", "b", " ", "c"};
Assert.assertArrayEquals(ArrayUtil.removeEmpty(a), resultA);
}
@Test
public void removeBlankTest() {
String[] a = {"a", "b", "", null, " ", "c"};
String[] resultA = {"a", "b", "c"};
Assert.assertArrayEquals(ArrayUtil.removeBlank(a), resultA);
}
@Test
public void nullToEmptyTest() {
String[] a = {"a", "b", "", null, " ", "c"};
String[] resultA = {"a", "b", "", "", " ", "c"};
Assert.assertArrayEquals(ArrayUtil.nullToEmpty(a), resultA);
}
@Test
public void wrapTest() {
Object a = new int[]{1, 2, 3, 4};
Object[] wrapA = ArrayUtil.wrap(a);
for (Object o : wrapA) {
Assert.assertTrue(o instanceof Integer);
}
}
}

View File

@ -38,4 +38,18 @@ public class CharUtilTest {
char a3 = '\u3000';
Assert.assertTrue(CharUtil.isBlankChar(a3));
}
@Test
public void toCloseCharTest(){
Assert.assertEquals('②', CharUtil.toCloseChar('2'));
Assert.assertEquals('Ⓜ', CharUtil.toCloseChar('M'));
Assert.assertEquals('ⓡ', CharUtil.toCloseChar('r'));
}
@Test
public void toCloseByNumberTest(){
Assert.assertEquals('②', CharUtil.toCloseByNumber(2));
Assert.assertEquals('⑫', CharUtil.toCloseByNumber(12));
Assert.assertEquals('⑳', CharUtil.toCloseByNumber(20));
}
}

View File

@ -7,7 +7,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-cron</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-crypto</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-db</artifactId>
@ -26,7 +26,7 @@
<mongo.version>3.12.8</mongo.version>
<sqlite.version>3.34.0</sqlite.version>
<hsqldb.version>2.5.1</hsqldb.version>
<jedis.version>3.5.1</jedis.version>
<jedis.version>3.5.2</jedis.version>
</properties>
<dependencies>
@ -79,8 +79,8 @@
</dependency>
<dependency>
<groupId>com.github.chris2018998</groupId>
<artifactId>BeeCP</artifactId>
<version>2.5.3</version>
<artifactId>beecp</artifactId>
<version>3.1.2</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
@ -148,7 +148,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.18.jre7</version>
<version>42.2.19.jre7</version>
<scope>test</scope>
</dependency>
<dependency>

View File

@ -33,11 +33,6 @@ public class BeeDSFactory extends AbstractDSFactory {
final BeeDataSourceConfig beeConfig = new BeeDataSourceConfig(driver, jdbcUrl, user, pass);
poolSetting.toBean(beeConfig);
// 修复BeeCP默认参数无效问题
if(beeConfig.getBorrowConcurrentSize() > beeConfig.getMaxActive()){
beeConfig.setMaxActive(beeConfig.getBorrowConcurrentSize() + 1);
}
// remarks等特殊配置since 5.3.8
String connValue;
for (String key : KEY_CONN_PROPS) {

View File

@ -7,7 +7,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-dfa</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-extra</artifactId>
@ -30,7 +30,7 @@
<net.version>3.7.2</net.version>
<emoji-java.version>5.1.1</emoji-java.version>
<servlet-api.version>4.0.1</servlet-api.version>
<spring-boot.version>2.4.3</spring-boot.version>
<spring-boot.version>2.4.4</spring-boot.version>
<cglib.version>3.3.0</cglib.version>
</properties>
@ -241,7 +241,7 @@
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-smartcn</artifactId>
<version>8.7.0</version>
<version>8.8.1</version>
<optional>true</optional>
</dependency>
<dependency>
@ -426,7 +426,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.2</version>
<version>5.3.5</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
@ -448,7 +448,7 @@
<dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
<version>1.8</version>
<version>1.9</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -320,7 +320,7 @@ public class QrCodeUtil {
// ------------------------------------------------------------------------------------------------------------------- decode
/**
* 解码二维码图片为文本
* 解码二维码或条形码图片为文本
*
* @param qrCodeInputstream 二维码输入流
* @return 解码文本
@ -330,7 +330,7 @@ public class QrCodeUtil {
}
/**
* 解码二维码图片为文本
* 解码二维码或条形码图片为文本
*
* @param qrCodeFile 二维码文件
* @return 解码文本
@ -340,7 +340,7 @@ public class QrCodeUtil {
}
/**
* 将二维码图片解码为文本
* 将二维码或条形码图片解码为文本
*
* @param image {@link Image} 二维码图片
* @return 解码后的文本
@ -350,7 +350,7 @@ public class QrCodeUtil {
}
/**
* 将二维码图片解码为文本
* 将二维码或条形码图片解码为文本
*
* @param image {@link Image} 二维码图片
* @param isTryHarder 是否优化精度

View File

@ -58,6 +58,7 @@ public class ServletUtil {
public static final String METHOD_TRACE = "TRACE";
// --------------------------------------------------------- getParam start
/**
* 获得所有请求参数
*
@ -117,6 +118,7 @@ public class ServletUtil {
// --------------------------------------------------------- getParam end
// --------------------------------------------------------- fillBean start
/**
* ServletRequest 参数转Bean
*
@ -276,6 +278,7 @@ public class ServletUtil {
}
// --------------------------------------------------------- Header start
/**
* 获取请求所有的头header信息
*
@ -402,6 +405,7 @@ public class ServletUtil {
// --------------------------------------------------------- Header end
// --------------------------------------------------------- Cookie start
/**
* 获得指定的Cookie
*
@ -488,6 +492,7 @@ public class ServletUtil {
// --------------------------------------------------------- Cookie end
// --------------------------------------------------------- Response start
/**
* 获得PrintWriter
*
@ -624,7 +629,7 @@ public class ServletUtil {
response.setHeader(name, (String) value);
} else if (Date.class.isAssignableFrom(value.getClass())) {
response.setDateHeader(name, ((Date) value).getTime());
} else if (value instanceof Integer || "int".equals(value.getClass().getSimpleName().toLowerCase())) {
} else if (value instanceof Integer || "int".equalsIgnoreCase(value.getClass().getSimpleName())) {
response.setIntHeader(name, (int) value);
} else {
response.setHeader(name, value.toString());

View File

@ -9,6 +9,8 @@ import cn.hutool.extra.template.TemplateConfig.ResourceMode;
import cn.hutool.extra.template.TemplateEngine;
import com.jfinal.template.source.FileSourceFactory;
import java.io.File;
/**
* Enjoy库的引擎包装
*
@ -99,7 +101,8 @@ public class EnjoyEngine implements TemplateEngine {
break;
case WEB_ROOT:
engine.setSourceFactory(new FileSourceFactory());
engine.setBaseTemplatePath(FileUtil.getAbsolutePath(FileUtil.getWebRoot()));
final File root = FileUtil.file(FileUtil.getWebRoot(), config.getPath());
engine.setBaseTemplatePath(FileUtil.getAbsolutePath(root));
break;
default:
break;

View File

@ -1,11 +1,16 @@
package cn.hutool.extra.template.engine.wit;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateException;
import org.febit.wit.Engine;
import org.febit.wit.exceptions.ResourceNotFoundException;
import org.febit.wit.util.Props;
import java.io.File;
/**
* Wit(http://zqq90.github.io/webit-script/)模板引擎封装
@ -75,10 +80,34 @@ public class WitEngine implements TemplateEngine {
* @return {@link Engine}
*/
private static Engine createEngine(TemplateConfig config) {
if (null == config) {
config = TemplateConfig.DEFAULT;
final Props configProps = Engine.createConfigProps("");
Dict dict = null;
if (null != config) {
dict = Dict.create();
// 自定义编码
dict.set("DEFAULT_ENCODING", config.getCharset());
switch (config.getResourceMode()){
case CLASSPATH:
configProps.set("pathLoader.root", config.getPath());
configProps.set("routeLoader.defaultLoader", "classpathLoader");
break;
case STRING:
configProps.set("routeLoader.defaultLoader", "stringLoader");
break;
case FILE:
configProps.set("pathLoader.root", config.getPath());
configProps.set("routeLoader.defaultLoader", "fileLoader");
break;
case WEB_ROOT:
final File root = FileUtil.file(FileUtil.getWebRoot(), config.getPath());
configProps.set("pathLoader.root", FileUtil.getAbsolutePath(root));
configProps.set("routeLoader.defaultLoader", "fileLoader");
break;
}
}
return Engine.create();
return Engine.create(configProps,dict);
}
}

View File

@ -54,6 +54,14 @@ public class QrCodeUtilTest {
Console.log(decode);
}
@Test
@Ignore
public void decodeTest2() {
// 条形码
String decode = QrCodeUtil.decode(FileUtil.file("d:/test/90.png"));
Console.log(decode);
}
@Test
@Ignore
public void generateAsBase64Test(){

View File

@ -1,5 +1,6 @@
package cn.hutool.extra.servlet;
import org.junit.Ignore;
import org.junit.Test;
import javax.servlet.http.HttpServletResponse;
@ -8,18 +9,22 @@ import java.nio.charset.StandardCharsets;
/**
* ServletUtil工具类测试
*
* @author dazer
* @date 2021/3/24 15:02
* @see ServletUtil
*/
public class ServletUtilTest {
@Test
@Ignore
public void writeTest() {
HttpServletResponse response = null;
byte[] bytes = new String("地球是我们共同的家园,需要大家珍惜.").getBytes(StandardCharsets.UTF_8);
byte[] bytes = "地球是我们共同的家园,需要大家珍惜.".getBytes(StandardCharsets.UTF_8);
//下载文件
// 这里没法直接测试直接写到这里方便调用
//noinspection ConstantConditions
if (response != null) {
String fileName = "签名文件.pdf";
String contentType = "application/pdf";// application/octet-streamimage/jpegimage/gif

View File

@ -157,10 +157,20 @@ public class TemplateUtilTest {
@Test
public void WitEngineTest() {
TemplateEngine engine = TemplateUtil.createEngine(
new TemplateConfig("templates", ResourceMode.CLASSPATH).setCustomEngine(WitEngine.class));
Template template = engine.getTemplate("/templates/wit_test.wit");
//classpath模板
TemplateConfig config = new TemplateConfig("templates", ResourceMode.CLASSPATH)
.setCustomEngine(WitEngine.class);
TemplateEngine engine = TemplateUtil.createEngine(config);
Template template = engine.getTemplate("/wit_test.wit");
String result = template.render(Dict.create().set("name", "hutool"));
Assert.assertEquals("hello,hutool", StrUtil.trim(result));
// 字符串模板
config = new TemplateConfig("templates", ResourceMode.STRING)
.setCustomEngine(WitEngine.class);
engine = TemplateUtil.createEngine(config);
template = engine.getTemplate("<%var name;%>hello,${name}");
result = template.render(Dict.create().set("name", "hutool"));
Assert.assertEquals("hello,hutool", StrUtil.trim(result));
}
}

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-http</artifactId>

View File

@ -318,4 +318,11 @@ public class HttpUtilTest {
final String s = HttpUtil.get(url);
Console.log(s);
}
@Test
@Ignore
public void sinajsTest(){
final String s = HttpUtil.get("http://hq.sinajs.cn/list=sh600519");
Console.log(s);
}
}

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-json</artifactId>

View File

@ -50,7 +50,9 @@ final class InternalJSONUtil {
} else if (value instanceof Iterable || value instanceof Iterator || value.getClass().isArray()) {
new JSONArray(value).write(writer, indentFactor, indent);
} else if (value instanceof Number) {
writer.write(NumberUtil.toStr((Number) value));
// since 5.6.2可配置是否去除末尾多余0例如如果为true,5.0返回5
final boolean isStripTrailingZeros = null == config || config.isStripTrailingZeros();
writer.write(NumberUtil.toStr((Number) value, isStripTrailingZeros));
} else if (value instanceof Date || value instanceof Calendar || value instanceof TemporalAccessor) {
final String format = (null == config) ? null : config.getDateFormat();
writer.write(formatDate(value, format));

View File

@ -36,6 +36,11 @@ public class JSONConfig implements Serializable {
*/
private boolean transientSupport = true;
/**
* 是否去除末尾多余0例如如果为true,5.0返回5
*/
private boolean stripTrailingZeros = true;
/**
* 创建默认的配置项
*
@ -192,4 +197,23 @@ public class JSONConfig implements Serializable {
this.transientSupport = transientSupport;
return this;
}
/**
* 是否去除末尾多余0例如如果为true,5.0返回5
* @return 是否去除末尾多余0例如如果为true,5.0返回5
* @since 5.6.2
*/
public boolean isStripTrailingZeros() {
return stripTrailingZeros;
}
/**
* 设置是否去除末尾多余0例如如果为true,5.0返回5
* @param stripTrailingZeros 是否去除末尾多余0例如如果为true,5.0返回5
* @since 5.6.2
*/
public JSONConfig setStripTrailingZeros(boolean stripTrailingZeros) {
this.stripTrailingZeros = stripTrailingZeros;
return this;
}
}

View File

@ -173,6 +173,23 @@ public class JSONUtilTest {
Assert.assertEquals("{\"test2\":12.0}", jsonObject.toString());
}
@Test
public void setStripTrailingZerosTest() {
// 默认去除多余的0
final JSONObject jsonObjectDefault = JSONUtil.createObj()
.set("test2", 12.00D);
Assert.assertEquals("{\"test2\":12}", jsonObjectDefault.toString());
// 不去除多余的0
final JSONObject jsonObject = JSONUtil.createObj(JSONConfig.create().setStripTrailingZeros(false))
.set("test2", 12.00D);
Assert.assertEquals("{\"test2\":12.0}", jsonObject.toString());
// 去除多余的0
jsonObject.getConfig().setStripTrailingZeros(true);
Assert.assertEquals("{\"test2\":12}", jsonObject.toString());
}
@Test
public void parseObjTest() {
// 测试转义

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-log</artifactId>

View File

@ -8,7 +8,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-poi</artifactId>
@ -44,7 +44,7 @@
<dependency>
<groupId>org.ofdrw</groupId>
<artifactId>ofdrw-full</artifactId>
<version>1.7.3</version>
<version>1.7.4</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>

View File

@ -8,7 +8,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-script</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-setting</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-socket</artifactId>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
</parent>
<artifactId>hutool-system</artifactId>
@ -26,7 +26,7 @@
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>5.6.0</version>
<version>5.6.1</version>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@ -8,7 +8,7 @@
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
<version>5.6.3-SNAPSHOT</version>
<name>hutool</name>
<description>Hutool是一个小而全的Java工具类库通过静态方法封装降低相关API的学习成本提高工作效率使Java拥有函数式语言般的优雅让Java语言也可以“甜甜的”。</description>
<url>https://github.com/looly/hutool</url>