mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
Merge remote-tracking branch 'upstream/v5-dev' into v5-dev
This commit is contained in:
commit
bb5b524418
30
CHANGELOG.md
30
CHANGELOG.md
@ -3,17 +3,45 @@
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
# 5.5.9 (2021-02-18)
|
# 5.6.0 (2021-03-10)
|
||||||
|
|
||||||
|
### 新特性
|
||||||
|
* 【poi 】 重要:不再兼容POI-3.x,增加兼容POI-5.x(issue#I35J6B@Gitee)
|
||||||
|
* 【core 】 FileTypeUtil使用长匹配优先(pr#1457@Github)
|
||||||
|
* 【core 】 IterUtil和CollUtil增加isEqualList方法(issue#I3A3PY@Gitee)
|
||||||
|
* 【crypto 】 增加PBKDF2(issue#1416@Github)
|
||||||
|
* 【core 】 增加FuncKeyMap(issue#1402@Github)
|
||||||
|
* 【core 】 增加StrMatcher(issue#1379@Github)
|
||||||
|
* 【core 】 NumberUtil增加factorial针对BigInterger方法(issue#1379@Github)
|
||||||
|
* 【core 】 TreeNode增加equals方法(issue#1467@Github)
|
||||||
|
|
||||||
|
### Bug修复
|
||||||
|
* 【socket 】 修复Client创建失败资源未释放问题。
|
||||||
|
* 【core 】 修复DataSizeUtil中EB单位错误问题(issue#I39O7I@Gitee)
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 5.5.9 (2021-02-26)
|
||||||
|
|
||||||
### 新特性
|
### 新特性
|
||||||
* 【crypto 】 PemUtil.readPemKey支持EC(pr#1366@Github)
|
* 【crypto 】 PemUtil.readPemKey支持EC(pr#1366@Github)
|
||||||
* 【extra 】 Ftp等cd方法增加同步(issue#1397@Github)
|
* 【extra 】 Ftp等cd方法增加同步(issue#1397@Github)
|
||||||
* 【core 】 StrUtil增加endWithAnyIgnoreCase(issue#I37I0B@Gitee)
|
* 【core 】 StrUtil增加endWithAnyIgnoreCase(issue#I37I0B@Gitee)
|
||||||
|
* 【crypto 】 Sm2增加getD和getQ方法(issue#I37Z4C@Gitee)
|
||||||
|
* 【cache 】 AbstractCache增加keySet方法(issue#I37Z4C@Gitee)
|
||||||
|
* 【core 】 NumberWordFormatter增加formatSimple方法(pr#1436@Github)
|
||||||
|
* 【crypto 】 增加读取openSSL生成的sm2私钥
|
||||||
|
* 【crypto 】 增加众多方法,SM2兼容各类密钥格式(issue#I37Z75@Gitee)
|
||||||
|
|
||||||
### Bug修复
|
### Bug修复
|
||||||
* 【json 】 JSONUtil.isJson方法改变trim策略,解决特殊空白符导致判断失败问题
|
* 【json 】 JSONUtil.isJson方法改变trim策略,解决特殊空白符导致判断失败问题
|
||||||
* 【json 】 修复SQLEXception导致的栈溢出(issue#1399@Github)
|
* 【json 】 修复SQLEXception导致的栈溢出(issue#1399@Github)
|
||||||
* 【extra 】 修复Ftp中异常参数没有传入问题(issue#1397@Github)
|
* 【extra 】 修复Ftp中异常参数没有传入问题(issue#1397@Github)
|
||||||
|
* 【crypto 】 修复Sm2使用D构造空指针问题(issue#I37Z4C@Gitee)
|
||||||
|
* 【poi 】 修复ExcelPicUtil中图表报错问题(issue#I38857@Gitee)
|
||||||
|
* 【core 】 修复ListUtil.page方法返回空列表无法编辑问题(issue#1415@Github)
|
||||||
|
* 【core 】 修复ListUtil.sub中step不通结果不一致问题(issue#1409@Github)
|
||||||
|
* 【db 】 修复Condition转换参数值时未转换数字异常(issue#I38LTM@Gitee)
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -125,19 +125,19 @@ Each module can be introduced individually, or all modules can be introduced by
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-all</artifactId>
|
<artifactId>hutool-all</artifactId>
|
||||||
<version>5.5.9</version>
|
<version>5.6.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Gradle
|
### Gradle
|
||||||
```
|
```
|
||||||
compile 'cn.hutool:hutool-all:5.5.9'
|
compile 'cn.hutool:hutool-all:5.6.0'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
|
||||||
- [Maven1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.5.9/)
|
- [Maven1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.6.0/)
|
||||||
- [Maven2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.5.9/)
|
- [Maven2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.6.0/)
|
||||||
|
|
||||||
> note:
|
> 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.
|
> Hutool 5.x supports JDK8+ and is not tested on Android platforms, and cannot guarantee that all tool classes or tool methods are available.
|
||||||
|
@ -123,21 +123,21 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-all</artifactId>
|
<artifactId>hutool-all</artifactId>
|
||||||
<version>5.5.9</version>
|
<version>5.6.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Gradle
|
### Gradle
|
||||||
```
|
```
|
||||||
compile 'cn.hutool:hutool-all:5.5.9'
|
compile 'cn.hutool:hutool-all:5.6.0'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 非Maven项目
|
### 非Maven项目
|
||||||
|
|
||||||
点击以下任一链接,下载`hutool-all-X.X.X.jar`即可:
|
点击以下任一链接,下载`hutool-all-X.X.X.jar`即可:
|
||||||
|
|
||||||
- [Maven中央库1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.5.9/)
|
- [Maven中央库1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.6.0/)
|
||||||
- [Maven中央库2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.5.9/)
|
- [Maven中央库2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.6.0/)
|
||||||
|
|
||||||
> 注意
|
> 注意
|
||||||
> Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。
|
> Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。
|
||||||
|
@ -1 +1 @@
|
|||||||
5.5.9
|
5.6.0
|
||||||
|
@ -1 +1 @@
|
|||||||
var version = '5.5.9'
|
var version = '5.6.0'
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-all</artifactId>
|
<artifactId>hutool-all</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-aop</artifactId>
|
<artifactId>hutool-aop</artifactId>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-bloomFilter</artifactId>
|
<artifactId>hutool-bloomFilter</artifactId>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-bom</artifactId>
|
<artifactId>hutool-bom</artifactId>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-cache</artifactId>
|
<artifactId>hutool-cache</artifactId>
|
||||||
|
@ -7,6 +7,7 @@ import cn.hutool.core.lang.func.Func0;
|
|||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
@ -307,11 +308,22 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
* @return this
|
* @return this
|
||||||
* @since 5.5.2
|
* @since 5.5.2
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public AbstractCache<K, V> setListener(CacheListener<K, V> listener) {
|
public AbstractCache<K, V> setListener(CacheListener<K, V> listener) {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回所有键
|
||||||
|
*
|
||||||
|
* @return 所有键
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public Set<K> keySet(){
|
||||||
|
return this.cacheMap.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对象移除回调。默认无动作<br>
|
* 对象移除回调。默认无动作<br>
|
||||||
* 子类可重写此方法用于监听移除事件,如果重写,listener将无效
|
* 子类可重写此方法用于监听移除事件,如果重写,listener将无效
|
||||||
|
@ -48,7 +48,7 @@ public class LRUCache<K, V> extends AbstractCache<K, V> {
|
|||||||
// ---------------------------------------------------------------- prune
|
// ---------------------------------------------------------------- prune
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 只清理超时对象,LRU的实现会交给<code>LinkedHashMap</code>
|
* 只清理超时对象,LRU的实现会交给{@code LinkedHashMap}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected int pruneCache() {
|
protected int pruneCache() {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-captcha</artifactId>
|
<artifactId>hutool-captcha</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-core</artifactId>
|
<artifactId>hutool-core</artifactId>
|
||||||
|
@ -60,7 +60,6 @@ public class CompareToBuilder implements Builder<Integer> {
|
|||||||
* 构造,构造后调用append方法增加比较项,然后调用{@link #toComparison()}获取结果
|
* 构造,构造后调用append方法增加比较项,然后调用{@link #toComparison()}获取结果
|
||||||
*/
|
*/
|
||||||
public CompareToBuilder() {
|
public CompareToBuilder() {
|
||||||
super();
|
|
||||||
comparison = 0;
|
comparison = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ public class HashCodeBuilder implements Builder<Integer> {
|
|||||||
*
|
*
|
||||||
* @since 2.3
|
* @since 2.3
|
||||||
*/
|
*/
|
||||||
private static final ThreadLocal<Set<IDKey>> REGISTRY = new ThreadLocal<Set<IDKey>>();
|
private static final ThreadLocal<Set<IDKey>> REGISTRY = new ThreadLocal<>();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode()
|
* NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode()
|
||||||
|
@ -2780,11 +2780,11 @@ public class CollUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取最大值
|
* 取最小值
|
||||||
*
|
*
|
||||||
* @param <T> 元素类型
|
* @param <T> 元素类型
|
||||||
* @param coll 集合
|
* @param coll 集合
|
||||||
* @return 最大值
|
* @return 最小值
|
||||||
* @see Collections#min(Collection)
|
* @see Collections#min(Collection)
|
||||||
* @since 4.6.5
|
* @since 4.6.5
|
||||||
*/
|
*/
|
||||||
@ -2988,4 +2988,25 @@ public class CollUtil {
|
|||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断两个{@link Collection} 是否元素和顺序相同,返回{@code true}的条件是:
|
||||||
|
* <ul>
|
||||||
|
* <li>两个{@link Collection}必须长度相同</li>
|
||||||
|
* <li>两个{@link Collection}元素相同index的对象必须equals,满足{@link Objects#equals(Object, Object)}</li>
|
||||||
|
* </ul>
|
||||||
|
* 此方法来自Apache-Commons-Collections4。
|
||||||
|
*
|
||||||
|
* @param list1 列表1
|
||||||
|
* @param list2 列表2
|
||||||
|
* @return 是否相同
|
||||||
|
* @since 5.6.0
|
||||||
|
*/
|
||||||
|
public static boolean isEqualList(final Collection<?> list1, final Collection<?> list2) {
|
||||||
|
if (list1 == null || list2 == null || list1.size() != list2.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IterUtil.isEqualList(list1, list2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -612,7 +613,7 @@ public class IterUtil {
|
|||||||
/**
|
/**
|
||||||
* Enumeration转换为Iterator
|
* Enumeration转换为Iterator
|
||||||
* <p>
|
* <p>
|
||||||
* Adapt the specified <code>Enumeration</code> to the <code>Iterator</code> interface
|
* Adapt the specified {@code Enumeration} to the {@code Iterator} interface
|
||||||
*
|
*
|
||||||
* @param <E> 集合元素类型
|
* @param <E> 集合元素类型
|
||||||
* @param e {@link Enumeration}
|
* @param e {@link Enumeration}
|
||||||
@ -859,4 +860,39 @@ public class IterUtil {
|
|||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断两个{@link Iterable} 是否元素和顺序相同,返回{@code true}的条件是:
|
||||||
|
* <ul>
|
||||||
|
* <li>两个{@link Iterable}必须长度相同</li>
|
||||||
|
* <li>两个{@link Iterable}元素相同index的对象必须equals,满足{@link Objects#equals(Object, Object)}</li>
|
||||||
|
* </ul>
|
||||||
|
* 此方法来自Apache-Commons-Collections4。
|
||||||
|
*
|
||||||
|
* @param list1 列表1
|
||||||
|
* @param list2 列表2
|
||||||
|
* @return 是否相同
|
||||||
|
* @since 5.6.0
|
||||||
|
*/
|
||||||
|
public static boolean isEqualList(final Iterable<?> list1, final Iterable<?> list2) {
|
||||||
|
if (list1 == list2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Iterator<?> it1 = list1.iterator();
|
||||||
|
final Iterator<?> it2 = list2.iterator();
|
||||||
|
Object obj1;
|
||||||
|
Object obj2;
|
||||||
|
while (it1.hasNext() && it2.hasNext()) {
|
||||||
|
obj1 = it1.next();
|
||||||
|
obj2 = it2.next();
|
||||||
|
|
||||||
|
if (false == Objects.equals(obj1, obj2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当两个Iterable长度不一致时返回false
|
||||||
|
return false == (it1.hasNext() || it2.hasNext());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,7 +246,7 @@ public class ListUtil {
|
|||||||
// 每页条目数大于总数直接返回所有
|
// 每页条目数大于总数直接返回所有
|
||||||
if (resultSize <= pageSize) {
|
if (resultSize <= pageSize) {
|
||||||
if (pageNo < (PageUtil.getFirstPageNo() + 1)) {
|
if (pageNo < (PageUtil.getFirstPageNo() + 1)) {
|
||||||
return Collections.unmodifiableList(list);
|
return unmodifiable(list);
|
||||||
} else {
|
} else {
|
||||||
// 越界直接返回空
|
// 越界直接返回空
|
||||||
return new ArrayList<>(0);
|
return new ArrayList<>(0);
|
||||||
@ -262,11 +262,11 @@ public class ListUtil {
|
|||||||
if (startEnd[1] > resultSize) {
|
if (startEnd[1] > resultSize) {
|
||||||
startEnd[1] = resultSize;
|
startEnd[1] = resultSize;
|
||||||
if (startEnd[0] > startEnd[1]) {
|
if (startEnd[0] > startEnd[1]) {
|
||||||
return empty();
|
return new ArrayList<>(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return list.subList(startEnd[0], startEnd[1]);
|
return sub(list, startEnd[0], startEnd[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -366,7 +366,8 @@ public class ListUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 截取集合的部分
|
* 截取集合的部分<br>
|
||||||
|
* 此方法与{@link List#subList(int, int)} 不同在于子列表是新的副本,操作子列表不会影响原列表。
|
||||||
*
|
*
|
||||||
* @param <T> 集合元素类型
|
* @param <T> 集合元素类型
|
||||||
* @param list 被截取的数组
|
* @param list 被截取的数组
|
||||||
@ -407,8 +408,8 @@ public class ListUtil {
|
|||||||
end = size;
|
end = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (step <= 1) {
|
if (step < 1) {
|
||||||
return list.subList(start, end);
|
step = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<T> result = new ArrayList<>();
|
final List<T> result = new ArrayList<>();
|
||||||
|
@ -21,7 +21,6 @@ public class ComparableComparator<E extends Comparable<? super E>> implements Co
|
|||||||
* 构造
|
* 构造
|
||||||
*/
|
*/
|
||||||
public ComparableComparator() {
|
public ComparableComparator() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -963,6 +963,21 @@ public class Convert {
|
|||||||
return NumberWordFormatter.format(number);
|
return NumberWordFormatter.format(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将阿拉伯数字转为精简表示形式,例如:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1200 -》 1.2k
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param number {@link Number}对象
|
||||||
|
* @return 英文表达式
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static String numberToSimple(Number number) {
|
||||||
|
return NumberWordFormatter.formatSimple(number.longValue());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将阿拉伯数字转为中文表达方式
|
* 将阿拉伯数字转为中文表达方式
|
||||||
*
|
*
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
package cn.hutool.core.convert;
|
package cn.hutool.core.convert;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.NumberUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将浮点数类型的number转换成英语的表达方式 <br>
|
* 将浮点数类型的number转换成英语的表达方式 <br>
|
||||||
* 参考博客:http://blog.csdn.net/eric_sunah/article/details/8713226
|
* 参考博客:http://blog.csdn.net/eric_sunah/article/details/8713226
|
||||||
*
|
*
|
||||||
* @author Looly
|
* @author Looly,totalo
|
||||||
* @since 3.0.9
|
* @since 3.0.9
|
||||||
*/
|
*/
|
||||||
public class NumberWordFormatter {
|
public class NumberWordFormatter {
|
||||||
@ -33,33 +32,37 @@ public class NumberWordFormatter {
|
|||||||
if (x != null) {
|
if (x != null) {
|
||||||
return format(x.toString());
|
return format(x.toString());
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return StrUtil.EMPTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将阿拉伯数字转化为简介计数单位,例如 2100 => 2.1k
|
* 将阿拉伯数字转化为简洁计数单位,例如 2100 =》 2.1k
|
||||||
* 范围默认只到w
|
* 范围默认只到w
|
||||||
* @param value
|
*
|
||||||
* @return
|
* @param value 被格式化的数字
|
||||||
|
* @return 格式化后的数字
|
||||||
|
* @since 5.5.9
|
||||||
*/
|
*/
|
||||||
public static String formatValue(long value) {
|
public static String formatSimple(long value) {
|
||||||
return formatValue(value, true);
|
return formatSimple(value, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将阿拉伯数字转化为简介计数单位,例如 2100 => 2.1k
|
* 将阿拉伯数字转化为简介计数单位,例如 2100 =》 2.1k
|
||||||
|
*
|
||||||
* @param value 对应数字的值
|
* @param value 对应数字的值
|
||||||
* @param isTwo 控制是否为k、w
|
* @param isTwo 控制是否为只为k、w,例如当为{@code false}时返回4.38m,{@code true}返回438.43w
|
||||||
* @return
|
* @return 格式化后的数字
|
||||||
|
* @since 5.5.9
|
||||||
*/
|
*/
|
||||||
public static String formatValue(long value, boolean isTwo) {
|
public static String formatSimple(long value, boolean isTwo) {
|
||||||
if (value < 1000) {
|
if (value < 1000) {
|
||||||
return String.valueOf(value);
|
return String.valueOf(value);
|
||||||
}
|
}
|
||||||
int index = -1;
|
int index = -1;
|
||||||
double res = value * 1.0d;
|
double res = value;
|
||||||
while (res > 10 && (!isTwo || index < 1)) {
|
while (res > 10 && (false == isTwo || index < 1)) {
|
||||||
if (res > 1000) {
|
if (res > 1000) {
|
||||||
res = res / 1000;
|
res = res / 1000;
|
||||||
index++;
|
index++;
|
||||||
@ -69,8 +72,7 @@ public class NumberWordFormatter {
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DecimalFormat decimalFormat = new DecimalFormat("#.##");
|
return String.format("%s%s", NumberUtil.decimalFormat("#.##", res), NUMBER_SUFFIX[index]);
|
||||||
return String.format("%s%s", decimalFormat.format(res), NUMBER_SUFFIX[index]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -694,7 +694,6 @@ public class FastDatePrinter extends AbstractDateBasic implements DatePrinter {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
UnpaddedMonthField() {
|
UnpaddedMonthField() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -833,7 +832,6 @@ public class FastDatePrinter extends AbstractDateBasic implements DatePrinter {
|
|||||||
* Constructs an instance of {@code TwoDigitYearField}.
|
* Constructs an instance of {@code TwoDigitYearField}.
|
||||||
*/
|
*/
|
||||||
TwoDigitYearField() {
|
TwoDigitYearField() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -873,7 +871,6 @@ public class FastDatePrinter extends AbstractDateBasic implements DatePrinter {
|
|||||||
* Constructs an instance of {@code TwoDigitMonthField}.
|
* Constructs an instance of {@code TwoDigitMonthField}.
|
||||||
*/
|
*/
|
||||||
TwoDigitMonthField() {
|
TwoDigitMonthField() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,7 +27,6 @@ public final class FastStringWriter extends Writer {
|
|||||||
* @param initialSize 初始容量
|
* @param initialSize 初始容量
|
||||||
*/
|
*/
|
||||||
public FastStringWriter(int initialSize) {
|
public FastStringWriter(int initialSize) {
|
||||||
super();
|
|
||||||
if (initialSize < 0) {
|
if (initialSize < 0) {
|
||||||
initialSize = StrBuilder.DEFAULT_CAPACITY;
|
initialSize = StrBuilder.DEFAULT_CAPACITY;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import java.io.FileInputStream;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentSkipListMap;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
@ -23,7 +23,15 @@ public class FileTypeUtil {
|
|||||||
private static final Map<String, String> FILE_TYPE_MAP;
|
private static final Map<String, String> FILE_TYPE_MAP;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
FILE_TYPE_MAP = new ConcurrentHashMap<>();
|
FILE_TYPE_MAP = new ConcurrentSkipListMap<>((s1, s2) -> {
|
||||||
|
int len1 = s1.length();
|
||||||
|
int len2 = s2.length();
|
||||||
|
if (len1 == len2) {
|
||||||
|
return s1.compareTo(s2);
|
||||||
|
} else {
|
||||||
|
return len2 - len1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
FILE_TYPE_MAP.put("ffd8ff", "jpg"); // JPEG (jpg)
|
FILE_TYPE_MAP.put("ffd8ff", "jpg"); // JPEG (jpg)
|
||||||
FILE_TYPE_MAP.put("89504e47", "png"); // PNG (png)
|
FILE_TYPE_MAP.put("89504e47", "png"); // PNG (png)
|
||||||
@ -52,6 +60,7 @@ public class FileTypeUtil {
|
|||||||
FILE_TYPE_MAP.put("4d546864000000060001", "mid"); // MIDI (mid)
|
FILE_TYPE_MAP.put("4d546864000000060001", "mid"); // MIDI (mid)
|
||||||
FILE_TYPE_MAP.put("526172211a0700cf9073", "rar"); // WinRAR
|
FILE_TYPE_MAP.put("526172211a0700cf9073", "rar"); // WinRAR
|
||||||
FILE_TYPE_MAP.put("235468697320636f6e66", "ini");
|
FILE_TYPE_MAP.put("235468697320636f6e66", "ini");
|
||||||
|
FILE_TYPE_MAP.put("504B0304140000000800", "ofd"); // ofd文件 国标版式文件
|
||||||
FILE_TYPE_MAP.put("504B03040a0000000000", "jar");
|
FILE_TYPE_MAP.put("504B03040a0000000000", "jar");
|
||||||
FILE_TYPE_MAP.put("504B0304140008000800", "jar");
|
FILE_TYPE_MAP.put("504B0304140008000800", "jar");
|
||||||
// MS Excel 注意:word、msi 和 excel的文件头一样
|
// MS Excel 注意:word、msi 和 excel的文件头一样
|
||||||
@ -63,9 +72,9 @@ public class FileTypeUtil {
|
|||||||
FILE_TYPE_MAP.put("7061636b616765207765", "java"); // java文件
|
FILE_TYPE_MAP.put("7061636b616765207765", "java"); // java文件
|
||||||
FILE_TYPE_MAP.put("406563686f206f66660d", "bat"); // bat文件
|
FILE_TYPE_MAP.put("406563686f206f66660d", "bat"); // bat文件
|
||||||
FILE_TYPE_MAP.put("1f8b0800000000000000", "gz"); // gz文件
|
FILE_TYPE_MAP.put("1f8b0800000000000000", "gz"); // gz文件
|
||||||
FILE_TYPE_MAP.put("cafebabe0000002e0041", "class");// bat文件
|
FILE_TYPE_MAP.put("cafebabe0000002e0041", "class"); // class文件
|
||||||
FILE_TYPE_MAP.put("49545346030000006000", "chm");// bat文件
|
FILE_TYPE_MAP.put("49545346030000006000", "chm"); // chm文件
|
||||||
FILE_TYPE_MAP.put("04000000010000001300", "mxp");// bat文件
|
FILE_TYPE_MAP.put("04000000010000001300", "mxp"); // mxp文件
|
||||||
FILE_TYPE_MAP.put("6431303a637265617465", "torrent");
|
FILE_TYPE_MAP.put("6431303a637265617465", "torrent");
|
||||||
FILE_TYPE_MAP.put("6D6F6F76", "mov"); // Quicktime (mov)
|
FILE_TYPE_MAP.put("6D6F6F76", "mov"); // Quicktime (mov)
|
||||||
FILE_TYPE_MAP.put("FF575043", "wpd"); // WordPerfect (wpd)
|
FILE_TYPE_MAP.put("FF575043", "wpd"); // WordPerfect (wpd)
|
||||||
@ -85,7 +94,7 @@ public class FileTypeUtil {
|
|||||||
* @return 之前已经存在的文件扩展名
|
* @return 之前已经存在的文件扩展名
|
||||||
*/
|
*/
|
||||||
public static String putFileType(String fileStreamHexHead, String extName) {
|
public static String putFileType(String fileStreamHexHead, String extName) {
|
||||||
return FILE_TYPE_MAP.put(fileStreamHexHead.toLowerCase(), extName);
|
return FILE_TYPE_MAP.put(fileStreamHexHead, extName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,7 +104,7 @@ public class FileTypeUtil {
|
|||||||
* @return 移除的文件扩展名
|
* @return 移除的文件扩展名
|
||||||
*/
|
*/
|
||||||
public static String removeFileType(String fileStreamHexHead) {
|
public static String removeFileType(String fileStreamHexHead) {
|
||||||
return FILE_TYPE_MAP.remove(fileStreamHexHead.toLowerCase());
|
return FILE_TYPE_MAP.remove(fileStreamHexHead);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,7 +19,6 @@ public class MultiFileResource extends MultiResource{
|
|||||||
* @param files 文件资源列表
|
* @param files 文件资源列表
|
||||||
*/
|
*/
|
||||||
public MultiFileResource(Collection<File> files) {
|
public MultiFileResource(Collection<File> files) {
|
||||||
super();
|
|
||||||
add(files);
|
add(files);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +28,6 @@ public class MultiFileResource extends MultiResource{
|
|||||||
* @param files 文件资源列表
|
* @param files 文件资源列表
|
||||||
*/
|
*/
|
||||||
public MultiFileResource(File... files) {
|
public MultiFileResource(File... files) {
|
||||||
super();
|
|
||||||
add(files);
|
add(files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ public enum DataUnit {
|
|||||||
*/
|
*/
|
||||||
TERABYTES("TB", DataSize.ofTerabytes(1));
|
TERABYTES("TB", DataSize.ofTerabytes(1));
|
||||||
|
|
||||||
public static final String[] UNIT_NAMES = new String[]{"B", "kB", "MB", "GB", "TB", "EB"};
|
public static final String[] UNIT_NAMES = new String[]{"B", "kB", "MB", "GB", "TB", "PB", "EB"};
|
||||||
|
|
||||||
private final String suffix;
|
private final String suffix;
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ public final class Holder<T> extends MutableObj<T>{
|
|||||||
* 构造
|
* 构造
|
||||||
*/
|
*/
|
||||||
public Holder() {
|
public Holder() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,7 +17,6 @@ public class MutableBool implements Comparable<MutableBool>, Mutable<Boolean>, S
|
|||||||
* 构造,默认值0
|
* 构造,默认值0
|
||||||
*/
|
*/
|
||||||
public MutableBool() {
|
public MutableBool() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +24,6 @@ public class MutableBool implements Comparable<MutableBool>, Mutable<Boolean>, S
|
|||||||
* @param value 值
|
* @param value 值
|
||||||
*/
|
*/
|
||||||
public MutableBool(final boolean value) {
|
public MutableBool(final boolean value) {
|
||||||
super();
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +33,6 @@ public class MutableBool implements Comparable<MutableBool>, Mutable<Boolean>, S
|
|||||||
* @throws NumberFormatException 转为Boolean错误
|
* @throws NumberFormatException 转为Boolean错误
|
||||||
*/
|
*/
|
||||||
public MutableBool(final String value) throws NumberFormatException {
|
public MutableBool(final String value) throws NumberFormatException {
|
||||||
super();
|
|
||||||
this.value = Boolean.parseBoolean(value);
|
this.value = Boolean.parseBoolean(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ public class MutableByte extends Number implements Comparable<MutableByte>, Muta
|
|||||||
* 构造,默认值0
|
* 构造,默认值0
|
||||||
*/
|
*/
|
||||||
public MutableByte() {
|
public MutableByte() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +24,6 @@ public class MutableByte extends Number implements Comparable<MutableByte>, Muta
|
|||||||
* @param value 值
|
* @param value 值
|
||||||
*/
|
*/
|
||||||
public MutableByte(final byte value) {
|
public MutableByte(final byte value) {
|
||||||
super();
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +41,6 @@ public class MutableByte extends Number implements Comparable<MutableByte>, Muta
|
|||||||
* @throws NumberFormatException 转为Byte错误
|
* @throws NumberFormatException 转为Byte错误
|
||||||
*/
|
*/
|
||||||
public MutableByte(final String value) throws NumberFormatException {
|
public MutableByte(final String value) throws NumberFormatException {
|
||||||
super();
|
|
||||||
this.value = Byte.parseByte(value);
|
this.value = Byte.parseByte(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ public class MutableDouble extends Number implements Comparable<MutableDouble>,
|
|||||||
* 构造,默认值0
|
* 构造,默认值0
|
||||||
*/
|
*/
|
||||||
public MutableDouble() {
|
public MutableDouble() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +24,6 @@ public class MutableDouble extends Number implements Comparable<MutableDouble>,
|
|||||||
* @param value 值
|
* @param value 值
|
||||||
*/
|
*/
|
||||||
public MutableDouble(final double value) {
|
public MutableDouble(final double value) {
|
||||||
super();
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +41,6 @@ public class MutableDouble extends Number implements Comparable<MutableDouble>,
|
|||||||
* @throws NumberFormatException 数字转换错误
|
* @throws NumberFormatException 数字转换错误
|
||||||
*/
|
*/
|
||||||
public MutableDouble(final String value) throws NumberFormatException {
|
public MutableDouble(final String value) throws NumberFormatException {
|
||||||
super();
|
|
||||||
this.value = Double.parseDouble(value);
|
this.value = Double.parseDouble(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ public class MutableFloat extends Number implements Comparable<MutableFloat>, Mu
|
|||||||
* 构造,默认值0
|
* 构造,默认值0
|
||||||
*/
|
*/
|
||||||
public MutableFloat() {
|
public MutableFloat() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +24,6 @@ public class MutableFloat extends Number implements Comparable<MutableFloat>, Mu
|
|||||||
* @param value 值
|
* @param value 值
|
||||||
*/
|
*/
|
||||||
public MutableFloat(final float value) {
|
public MutableFloat(final float value) {
|
||||||
super();
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +41,6 @@ public class MutableFloat extends Number implements Comparable<MutableFloat>, Mu
|
|||||||
* @throws NumberFormatException 数字转换错误
|
* @throws NumberFormatException 数字转换错误
|
||||||
*/
|
*/
|
||||||
public MutableFloat(final String value) throws NumberFormatException {
|
public MutableFloat(final String value) throws NumberFormatException {
|
||||||
super();
|
|
||||||
this.value = Float.parseFloat(value);
|
this.value = Float.parseFloat(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ public class MutableInt extends Number implements Comparable<MutableInt>, Mutabl
|
|||||||
* 构造,默认值0
|
* 构造,默认值0
|
||||||
*/
|
*/
|
||||||
public MutableInt() {
|
public MutableInt() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +24,6 @@ public class MutableInt extends Number implements Comparable<MutableInt>, Mutabl
|
|||||||
* @param value 值
|
* @param value 值
|
||||||
*/
|
*/
|
||||||
public MutableInt(final int value) {
|
public MutableInt(final int value) {
|
||||||
super();
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +41,6 @@ public class MutableInt extends Number implements Comparable<MutableInt>, Mutabl
|
|||||||
* @throws NumberFormatException 数字转换错误
|
* @throws NumberFormatException 数字转换错误
|
||||||
*/
|
*/
|
||||||
public MutableInt(final String value) throws NumberFormatException {
|
public MutableInt(final String value) throws NumberFormatException {
|
||||||
super();
|
|
||||||
this.value = Integer.parseInt(value);
|
this.value = Integer.parseInt(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
|
|||||||
* 构造,默认值0
|
* 构造,默认值0
|
||||||
*/
|
*/
|
||||||
public MutableLong() {
|
public MutableLong() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +24,6 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
|
|||||||
* @param value 值
|
* @param value 值
|
||||||
*/
|
*/
|
||||||
public MutableLong(final long value) {
|
public MutableLong(final long value) {
|
||||||
super();
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +41,6 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
|
|||||||
* @throws NumberFormatException 数字转换错误
|
* @throws NumberFormatException 数字转换错误
|
||||||
*/
|
*/
|
||||||
public MutableLong(final String value) throws NumberFormatException {
|
public MutableLong(final String value) throws NumberFormatException {
|
||||||
super();
|
|
||||||
this.value = Long.parseLong(value);
|
this.value = Long.parseLong(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ public class MutableObj<T> implements Mutable<T>, Serializable {
|
|||||||
* 构造,空值
|
* 构造,空值
|
||||||
*/
|
*/
|
||||||
public MutableObj() {
|
public MutableObj() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,7 +25,6 @@ public class MutableObj<T> implements Mutable<T>, Serializable {
|
|||||||
* @param value 值
|
* @param value 值
|
||||||
*/
|
*/
|
||||||
public MutableObj(final T value) {
|
public MutableObj(final T value) {
|
||||||
super();
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ public class MutableShort extends Number implements Comparable<MutableShort>, Mu
|
|||||||
* 构造,默认值0
|
* 构造,默认值0
|
||||||
*/
|
*/
|
||||||
public MutableShort() {
|
public MutableShort() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +24,6 @@ public class MutableShort extends Number implements Comparable<MutableShort>, Mu
|
|||||||
* @param value 值
|
* @param value 值
|
||||||
*/
|
*/
|
||||||
public MutableShort(final short value) {
|
public MutableShort(final short value) {
|
||||||
super();
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +41,6 @@ public class MutableShort extends Number implements Comparable<MutableShort>, Mu
|
|||||||
* @throws NumberFormatException 转为Short错误
|
* @throws NumberFormatException 转为Short错误
|
||||||
*/
|
*/
|
||||||
public MutableShort(final String value) throws NumberFormatException {
|
public MutableShort(final String value) throws NumberFormatException {
|
||||||
super();
|
|
||||||
this.value = Short.parseShort(value);
|
this.value = Short.parseShort(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ public class Tree<T> extends LinkedHashMap<String, Object> implements Node<T> {
|
|||||||
* @param treeNodeConfig TreeNode配置
|
* @param treeNodeConfig TreeNode配置
|
||||||
*/
|
*/
|
||||||
public Tree(TreeNodeConfig treeNodeConfig) {
|
public Tree(TreeNodeConfig treeNodeConfig) {
|
||||||
super();
|
|
||||||
this.treeNodeConfig = ObjectUtil.defaultIfNull(
|
this.treeNodeConfig = ObjectUtil.defaultIfNull(
|
||||||
treeNodeConfig, TreeNodeConfig.DEFAULT_CONFIG);
|
treeNodeConfig, TreeNodeConfig.DEFAULT_CONFIG);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package cn.hutool.core.lang.tree;
|
|||||||
|
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 树节点 每个属性都可以在{@link TreeNodeConfig}中被重命名<br>
|
* 树节点 每个属性都可以在{@link TreeNodeConfig}中被重命名<br>
|
||||||
@ -129,4 +130,21 @@ public class TreeNode<T> implements Node<T> {
|
|||||||
this.extra = extra;
|
this.extra = extra;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TreeNode<?> treeNode = (TreeNode<?>) o;
|
||||||
|
return Objects.equals(id, treeNode.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
45
hutool-core/src/main/java/cn/hutool/core/map/FuncKeyMap.java
Normal file
45
hutool-core/src/main/java/cn/hutool/core/map/FuncKeyMap.java
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package cn.hutool.core.map;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义函数Key风格的Map
|
||||||
|
*
|
||||||
|
* @param <K> 键类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
* @author Looly
|
||||||
|
* @since 5.6.0
|
||||||
|
*/
|
||||||
|
public class FuncKeyMap<K, V> extends CustomKeyMap<K, V> {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Function<Object, K> keyFunc;
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------- Constructor start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param m Map
|
||||||
|
* @param keyFunc 自定义KEY的函数
|
||||||
|
*/
|
||||||
|
public FuncKeyMap(Map<K, V> m, Function<Object, K> keyFunc) {
|
||||||
|
super(m);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------- Constructor end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Key转为驼峰风格,如果key为字符串的话
|
||||||
|
*
|
||||||
|
* @param key KEY
|
||||||
|
* @return 驼峰Key
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Object customKey(Object key) {
|
||||||
|
if (null != this.keyFunc) {
|
||||||
|
return keyFunc.apply(key);
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
116
hutool-core/src/main/java/cn/hutool/core/text/StrMatcher.java
Normal file
116
hutool-core/src/main/java/cn/hutool/core/text/StrMatcher.java
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package cn.hutool.core.text;
|
||||||
|
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串模式匹配,使用${XXXXX}作为变量,例如:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* pattern: ${name}-${age}-${gender}-${country}-${province}-${city}-${status}
|
||||||
|
* text: "小明-19-男-中国-河南-郑州-已婚"
|
||||||
|
* result: {name=小明, age=19, gender=男, country=中国, province=河南, city=郑州, status=已婚}
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.6.0
|
||||||
|
*/
|
||||||
|
public class StrMatcher {
|
||||||
|
|
||||||
|
List<String> patterns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param pattern 模式,变量用${XXX}占位
|
||||||
|
*/
|
||||||
|
public StrMatcher(String pattern) {
|
||||||
|
this.patterns = parse(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配并提取匹配到的内容
|
||||||
|
* @param text 被匹配的文本
|
||||||
|
* @return 匹配的map,key为变量名,value为匹配到的值
|
||||||
|
*/
|
||||||
|
public Map<String, String> match(String text) {
|
||||||
|
final HashMap<String, String> result = MapUtil.newHashMap(true);
|
||||||
|
int from = 0;
|
||||||
|
String key = null;
|
||||||
|
int to;
|
||||||
|
for (String part : patterns) {
|
||||||
|
if (StrUtil.isWrap(part, "${", "}")) {
|
||||||
|
// 变量
|
||||||
|
key = StrUtil.sub(part, 2, part.length() - 1);
|
||||||
|
} else {
|
||||||
|
to = text.indexOf(part, from);
|
||||||
|
if(to < 0){
|
||||||
|
//普通字符串未匹配到,说明整个模式不能匹配,返回空
|
||||||
|
return MapUtil.empty();
|
||||||
|
}
|
||||||
|
if (null != key && to > from) {
|
||||||
|
// 变量对应部分有内容
|
||||||
|
result.put(key, text.substring(from, to));
|
||||||
|
}
|
||||||
|
// 下一个起始点是普通字符串的末尾
|
||||||
|
from = to + part.length();
|
||||||
|
key = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null != key && from < text.length()) {
|
||||||
|
// 变量对应部分有内容
|
||||||
|
result.put(key, text.substring(from));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析表达式
|
||||||
|
* @param pattern 表达式,使用${XXXX}作为变量占位符
|
||||||
|
* @return 表达式
|
||||||
|
*/
|
||||||
|
private static List<String> parse(String pattern) {
|
||||||
|
List<String> patterns = new ArrayList<>();
|
||||||
|
final int length = pattern.length();
|
||||||
|
char c = 0;
|
||||||
|
char pre;
|
||||||
|
boolean inVar = false;
|
||||||
|
StrBuilder part = StrUtil.strBuilder();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
pre = c;
|
||||||
|
c = pattern.charAt(i);
|
||||||
|
if (inVar) {
|
||||||
|
part.append(c);
|
||||||
|
if ('}' == c) {
|
||||||
|
// 变量结束
|
||||||
|
inVar = false;
|
||||||
|
patterns.add(part.toString());
|
||||||
|
part.clear();
|
||||||
|
}
|
||||||
|
} else if ('{' == c && '$' == pre) {
|
||||||
|
// 变量开始
|
||||||
|
inVar = true;
|
||||||
|
final String preText = part.subString(0, part.length() - 1);
|
||||||
|
if (StrUtil.isNotEmpty(preText)) {
|
||||||
|
patterns.add(preText);
|
||||||
|
}
|
||||||
|
part.reset().append(pre).append(c);
|
||||||
|
} else {
|
||||||
|
// 普通字符
|
||||||
|
part.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part.length() > 0) {
|
||||||
|
patterns.add(part.toString());
|
||||||
|
}
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
}
|
@ -200,7 +200,7 @@ public class ExecutorBuilder implements Builder<ThreadPoolExecutor> {
|
|||||||
/**
|
/**
|
||||||
* 创建ExecutorBuilder,开始构建
|
* 创建ExecutorBuilder,开始构建
|
||||||
*
|
*
|
||||||
* @return {@link ExecutorBuilder}
|
* @return this
|
||||||
*/
|
*/
|
||||||
public static ExecutorBuilder create() {
|
public static ExecutorBuilder create() {
|
||||||
return new ExecutorBuilder();
|
return new ExecutorBuilder();
|
||||||
@ -227,7 +227,7 @@ public class ExecutorBuilder implements Builder<ThreadPoolExecutor> {
|
|||||||
/**
|
/**
|
||||||
* 构建ThreadPoolExecutor
|
* 构建ThreadPoolExecutor
|
||||||
*
|
*
|
||||||
* @param builder {@link ExecutorBuilder}
|
* @param builder this
|
||||||
* @return {@link ThreadPoolExecutor}
|
* @return {@link ThreadPoolExecutor}
|
||||||
*/
|
*/
|
||||||
private static ThreadPoolExecutor build(ExecutorBuilder builder) {
|
private static ThreadPoolExecutor build(ExecutorBuilder builder) {
|
||||||
|
@ -294,7 +294,7 @@ public class ClassUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找指定Public方法 如果找不到对应的方法或方法不为public的则返回<code>null</code>
|
* 查找指定Public方法 如果找不到对应的方法或方法不为public的则返回{@code null}
|
||||||
*
|
*
|
||||||
* @param clazz 类
|
* @param clazz 类
|
||||||
* @param methodName 方法名
|
* @param methodName 方法名
|
||||||
@ -341,7 +341,7 @@ public class ClassUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找指定类中的所有方法(包括非public方法),也包括父类和Object类的方法 找不到方法会返回<code>null</code>
|
* 查找指定类中的所有方法(包括非public方法),也包括父类和Object类的方法 找不到方法会返回{@code null}
|
||||||
*
|
*
|
||||||
* @param clazz 被查找的类
|
* @param clazz 被查找的类
|
||||||
* @param methodName 方法名
|
* @param methodName 方法名
|
||||||
@ -356,7 +356,7 @@ public class ClassUtil {
|
|||||||
// ----------------------------------------------------------------------------------------- Field
|
// ----------------------------------------------------------------------------------------- Field
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找指定类中的所有字段(包括非public字段), 字段不存在则返回<code>null</code>
|
* 查找指定类中的所有字段(包括非public字段), 字段不存在则返回{@code null}
|
||||||
*
|
*
|
||||||
* @param clazz 被查找字段的类
|
* @param clazz 被查找字段的类
|
||||||
* @param fieldName 字段名
|
* @param fieldName 字段名
|
||||||
@ -558,7 +558,7 @@ public class ClassUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 比较判断types1和types2两组类,如果types1中所有的类都与types2对应位置的类相同,或者是其父类或接口,则返回<code>true</code>
|
* 比较判断types1和types2两组类,如果types1中所有的类都与types2对应位置的类相同,或者是其父类或接口,则返回{@code true}
|
||||||
*
|
*
|
||||||
* @param types1 类组1
|
* @param types1 类组1
|
||||||
* @param types2 类组2
|
* @param types2 类组2
|
||||||
@ -626,7 +626,7 @@ public class ClassUtil {
|
|||||||
* 非单例模式,如果是非静态方法,每次创建一个新对象
|
* 非单例模式,如果是非静态方法,每次创建一个新对象
|
||||||
*
|
*
|
||||||
* @param <T> 对象类型
|
* @param <T> 对象类型
|
||||||
* @param classNameWithMethodName 类名和方法名表达式,类名与方法名用<code>.</code>或<code>#</code>连接 例如:com.xiaoleilu.hutool.StrUtil.isEmpty 或 com.xiaoleilu.hutool.StrUtil#isEmpty
|
* @param classNameWithMethodName 类名和方法名表达式,类名与方法名用{@code .}或{@code #}连接 例如:com.xiaoleilu.hutool.StrUtil.isEmpty 或 com.xiaoleilu.hutool.StrUtil#isEmpty
|
||||||
* @param args 参数,必须严格对应指定方法的参数类型和数量
|
* @param args 参数,必须严格对应指定方法的参数类型和数量
|
||||||
* @return 返回结果
|
* @return 返回结果
|
||||||
*/
|
*/
|
||||||
|
@ -26,7 +26,7 @@ public class HexUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断给定字符串是否为16进制数<br>
|
* 判断给定字符串是否为16进制数<br>
|
||||||
* 如果是,需要使用对应数字类型对象的<code>decode</code>方法解码<br>
|
* 如果是,需要使用对应数字类型对象的{@code decode}方法解码<br>
|
||||||
* 例如:{@code Integer.decode}方法解码int类型的16进制数字
|
* 例如:{@code Integer.decode}方法解码int类型的16进制数字
|
||||||
*
|
*
|
||||||
* @param value 值
|
* @param value 值
|
||||||
@ -74,7 +74,7 @@ public class HexUtil {
|
|||||||
* 将字节数组转换为十六进制字符数组
|
* 将字节数组转换为十六进制字符数组
|
||||||
*
|
*
|
||||||
* @param data byte[]
|
* @param data byte[]
|
||||||
* @param toLowerCase <code>true</code> 传换成小写格式 , <code>false</code> 传换成大写格式
|
* @param toLowerCase {@code true} 传换成小写格式 , {@code false} 传换成大写格式
|
||||||
* @return 十六进制char[]
|
* @return 十六进制char[]
|
||||||
*/
|
*/
|
||||||
public static char[] encodeHex(byte[] data, boolean toLowerCase) {
|
public static char[] encodeHex(byte[] data, boolean toLowerCase) {
|
||||||
@ -116,7 +116,7 @@ public class HexUtil {
|
|||||||
* 将字节数组转换为十六进制字符串
|
* 将字节数组转换为十六进制字符串
|
||||||
*
|
*
|
||||||
* @param data byte[]
|
* @param data byte[]
|
||||||
* @param toLowerCase <code>true</code> 传换成小写格式 , <code>false</code> 传换成大写格式
|
* @param toLowerCase {@code true} 传换成小写格式 , {@code false} 传换成大写格式
|
||||||
* @return 十六进制String
|
* @return 十六进制String
|
||||||
*/
|
*/
|
||||||
public static String encodeHexStr(byte[] data, boolean toLowerCase) {
|
public static String encodeHexStr(byte[] data, boolean toLowerCase) {
|
||||||
|
@ -1428,10 +1428,62 @@ public class NumberUtil {
|
|||||||
|
|
||||||
// ------------------------------------------------------------------------------------------- others
|
// ------------------------------------------------------------------------------------------- others
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算阶乘
|
||||||
|
* <p>
|
||||||
|
* n! = n * (n-1) * ... * 2 * 1
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param n 阶乘起始
|
||||||
|
* @return 结果
|
||||||
|
* @since 5.6.0
|
||||||
|
*/
|
||||||
|
public static BigInteger factorial(BigInteger n) {
|
||||||
|
if(n.equals(BigInteger.ZERO)){
|
||||||
|
return BigInteger.ONE;
|
||||||
|
}
|
||||||
|
return factorial(n, BigInteger.ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算范围阶乘
|
* 计算范围阶乘
|
||||||
* <p>
|
* <p>
|
||||||
* factorial(start, end) = start * (start - 1) * ... * (end - 1)
|
* factorial(start, end) = start * (start - 1) * ... * (end + 1)
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param start 阶乘起始(包含)
|
||||||
|
* @param end 阶乘结束,必须小于起始(不包括)
|
||||||
|
* @return 结果
|
||||||
|
* @since 5.6.0
|
||||||
|
*/
|
||||||
|
public static BigInteger factorial(BigInteger start, BigInteger end) {
|
||||||
|
Assert.notNull(start, "Factorial start must be not null!");
|
||||||
|
Assert.notNull(end, "Factorial end must be not null!");
|
||||||
|
if(start.compareTo(BigInteger.ZERO) < 0 || end.compareTo(BigInteger.ZERO) < 0){
|
||||||
|
throw new IllegalArgumentException(StrUtil.format("Factorial start and end both must be > 0, but got start={}, end={}", start, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start.equals(BigInteger.ZERO)){
|
||||||
|
start = BigInteger.ONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(end.compareTo(BigInteger.ONE) < 0){
|
||||||
|
end = BigInteger.ONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger result = start;
|
||||||
|
end = end.add(BigInteger.ONE);
|
||||||
|
while(start.compareTo(end) > 0) {
|
||||||
|
start = start.subtract(BigInteger.ONE);
|
||||||
|
result = result.multiply(start);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算范围阶乘
|
||||||
|
* <p>
|
||||||
|
* factorial(start, end) = start * (start - 1) * ... * (end + 1)
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param start 阶乘起始(包含)
|
* @param start 阶乘起始(包含)
|
||||||
|
@ -3,8 +3,6 @@ package cn.hutool.core.util;
|
|||||||
import cn.hutool.core.lang.PatternPool;
|
import cn.hutool.core.lang.PatternPool;
|
||||||
import cn.hutool.core.lang.Validator;
|
import cn.hutool.core.lang.Validator;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 手机号工具类
|
* 手机号工具类
|
||||||
|
@ -3,8 +3,6 @@ package cn.hutool.core.codec;
|
|||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import cn.hutool.core.codec.BCD;
|
|
||||||
|
|
||||||
public class BCDTest {
|
public class BCDTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -11,6 +11,7 @@ import org.junit.Assert;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@ -694,4 +695,14 @@ public class CollUtilTest {
|
|||||||
|
|
||||||
Assert.assertEquals(0, CollUtil.page(3, 5, objects).size());
|
Assert.assertEquals(0, CollUtil.page(3, 5, objects).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void subtractToListTest(){
|
||||||
|
List<Long> list1 = Arrays.asList(1L, 2L, 3L);
|
||||||
|
List<Long> list2 = Arrays.asList(2L, 3L);
|
||||||
|
|
||||||
|
List<Long> result = CollUtil.subtractToList(list1, list2);
|
||||||
|
Assert.assertEquals(1, result.size());
|
||||||
|
Assert.assertEquals(1L, result.get(0), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,4 +101,15 @@ public class ListUtilTest {
|
|||||||
int[] d1 = ListUtil.page(0,8,a).stream().mapToInt(Integer::valueOf).toArray();
|
int[] d1 = ListUtil.page(0,8,a).stream().mapToInt(Integer::valueOf).toArray();
|
||||||
Assert.assertArrayEquals(new int[]{1,2,3,4,5},d1);
|
Assert.assertArrayEquals(new int[]{1,2,3,4,5},d1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void subTest(){
|
||||||
|
final List<Integer> of = ListUtil.of(1, 2, 3, 4);
|
||||||
|
final List<Integer> sub = ListUtil.sub(of, 2, 4);
|
||||||
|
sub.remove(0);
|
||||||
|
|
||||||
|
// 对子列表操作不影响原列表
|
||||||
|
Assert.assertEquals(4, of.size());
|
||||||
|
Assert.assertEquals(1, sub.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import cn.hutool.core.convert.Convert;
|
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,9 +3,6 @@ package cn.hutool.core.convert;
|
|||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import cn.hutool.core.convert.Converter;
|
|
||||||
import cn.hutool.core.convert.ConverterRegistry;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ConverterRegistry 单元测试
|
* ConverterRegistry 单元测试
|
||||||
* @author Looly
|
* @author Looly
|
||||||
|
@ -12,14 +12,23 @@ public class NumberWordFormatTest {
|
|||||||
|
|
||||||
String format2 = NumberWordFormatter.format("2100.00");
|
String format2 = NumberWordFormatter.format("2100.00");
|
||||||
Assert.assertEquals("TWO THOUSAND ONE HUNDRED AND CENTS ONLY", format2);
|
Assert.assertEquals("TWO THOUSAND ONE HUNDRED AND CENTS ONLY", format2);
|
||||||
|
}
|
||||||
|
|
||||||
String format3 = NumberWordFormatter.formatValue(4384324, false);
|
@Test
|
||||||
Assert.assertEquals("4.38m", format3);
|
public void formatSimpleTest() {
|
||||||
|
String format1 = NumberWordFormatter.formatSimple(1200, false);
|
||||||
|
Assert.assertEquals("1.2k", format1);
|
||||||
|
|
||||||
String format4 = NumberWordFormatter.formatValue(4384324);
|
String format2 = NumberWordFormatter.formatSimple(4384324, false);
|
||||||
|
Assert.assertEquals("4.38m", format2);
|
||||||
|
|
||||||
|
String format3 = NumberWordFormatter.formatSimple(4384324, true);
|
||||||
|
Assert.assertEquals("438.43w", format3);
|
||||||
|
|
||||||
|
String format4 = NumberWordFormatter.formatSimple(4384324);
|
||||||
Assert.assertEquals("438.43w", format4);
|
Assert.assertEquals("438.43w", format4);
|
||||||
|
|
||||||
String format5 = NumberWordFormatter.formatValue(438);
|
String format5 = NumberWordFormatter.formatSimple(438);
|
||||||
Assert.assertEquals("438", format5);
|
Assert.assertEquals("438", format5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,9 @@ import org.junit.Ignore;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件类型判断单元测试
|
* 文件类型判断单元测试
|
||||||
@ -41,4 +44,16 @@ public class FileTypeUtilTest {
|
|||||||
String type = FileTypeUtil.getType(file);
|
String type = FileTypeUtil.getType(file);
|
||||||
Console.log(type);
|
Console.log(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void ofdTest() {
|
||||||
|
File file = FileUtil.file("e:/test.ofd");
|
||||||
|
String hex = IoUtil.readHex28Upper(FileUtil.getInputStream(file));
|
||||||
|
Console.log(hex);
|
||||||
|
String type = FileTypeUtil.getType(file);
|
||||||
|
Console.log(type);
|
||||||
|
Assert.assertEquals("ofd", type);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,13 @@ public class DataSizeUtilTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void formatTest(){
|
public void formatTest(){
|
||||||
final String format = DataSizeUtil.format(Long.MAX_VALUE);
|
String format = DataSizeUtil.format(Long.MAX_VALUE);
|
||||||
Assert.assertEquals("8,192 EB", format);
|
Assert.assertEquals("8 EB", format);
|
||||||
|
|
||||||
|
format = DataSizeUtil.format(1024L * 1024 * 1024 * 1024 * 1024);
|
||||||
|
Assert.assertEquals("1 PB", format);
|
||||||
|
|
||||||
|
format = DataSizeUtil.format(1024L * 1024 * 1024 * 1024);
|
||||||
|
Assert.assertEquals("1 TB", format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package cn.hutool.core.lang;
|
package cn.hutool.core.lang;
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class ClassScanerTest {
|
public class ClassScanerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
package cn.hutool.core.text;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Console;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class StrMatcherTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void matcherTest(){
|
||||||
|
final StrMatcher strMatcher = new StrMatcher("${name}-${age}-${gender}-${country}-${province}-${city}-${status}");
|
||||||
|
final Map<String, String> match = strMatcher.match("小明-19-男-中国-河南-郑州-已婚");
|
||||||
|
Console.log(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void matcherTest2(){
|
||||||
|
// 当有无匹配项的时候,按照全不匹配对待
|
||||||
|
final StrMatcher strMatcher = new StrMatcher("${name}-${age}-${gender}-${country}-${province}-${city}-${status}");
|
||||||
|
final Map<String, String> match = strMatcher.match("小明-19-男-中国-河南-郑州");
|
||||||
|
Assert.assertEquals(0, match.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void matcherTest3(){
|
||||||
|
// 当有无匹配项的时候,按照全不匹配对待
|
||||||
|
final StrMatcher strMatcher = new StrMatcher("${name}经过${year}年");
|
||||||
|
final Map<String, String> match = strMatcher.match("小明经过20年,成长为一个大人。");
|
||||||
|
Console.log(match);
|
||||||
|
Assert.assertEquals("小明", match.get("name"));
|
||||||
|
Assert.assertEquals("20", match.get("year"));
|
||||||
|
}
|
||||||
|
}
|
@ -75,6 +75,7 @@ public class IdUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore
|
||||||
public void snowflakeBenchTest() {
|
public void snowflakeBenchTest() {
|
||||||
final Set<Long> set = new ConcurrentHashSet<>();
|
final Set<Long> set = new ConcurrentHashSet<>();
|
||||||
final Snowflake snowflake = IdUtil.createSnowflake(1, 1);
|
final Snowflake snowflake = IdUtil.createSnowflake(1, 1);
|
||||||
@ -105,6 +106,7 @@ public class IdUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore
|
||||||
public void snowflakeBenchTest2() {
|
public void snowflakeBenchTest2() {
|
||||||
final Set<Long> set = new ConcurrentHashSet<>();
|
final Set<Long> set = new ConcurrentHashSet<>();
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import org.junit.Assert;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -308,6 +309,24 @@ public class NumberUtilTest {
|
|||||||
Assert.assertEquals(2432902008176640000L, NumberUtil.factorial(20, 0));
|
Assert.assertEquals(2432902008176640000L, NumberUtil.factorial(20, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void factorialTest2(){
|
||||||
|
long factorial = NumberUtil.factorial(new BigInteger("0")).longValue();
|
||||||
|
Assert.assertEquals(1, factorial);
|
||||||
|
|
||||||
|
Assert.assertEquals(1L, NumberUtil.factorial(new BigInteger("1")).longValue());
|
||||||
|
Assert.assertEquals(1307674368000L, NumberUtil.factorial(new BigInteger("15")).longValue());
|
||||||
|
Assert.assertEquals(2432902008176640000L, NumberUtil.factorial(20));
|
||||||
|
|
||||||
|
factorial = NumberUtil.factorial(new BigInteger("5"), new BigInteger("0")).longValue();
|
||||||
|
Assert.assertEquals(120, factorial);
|
||||||
|
factorial = NumberUtil.factorial(new BigInteger("5"), BigInteger.ONE).longValue();
|
||||||
|
Assert.assertEquals(120, factorial);
|
||||||
|
|
||||||
|
Assert.assertEquals(5, NumberUtil.factorial(new BigInteger("5"), new BigInteger("4")).longValue());
|
||||||
|
Assert.assertEquals(2432902008176640000L, NumberUtil.factorial(new BigInteger("20"), BigInteger.ZERO).longValue());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mulTest(){
|
public void mulTest(){
|
||||||
final BigDecimal mul = NumberUtil.mul(new BigDecimal("10"), null);
|
final BigDecimal mul = NumberUtil.mul(new BigDecimal("10"), null);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-cron</artifactId>
|
<artifactId>hutool-cron</artifactId>
|
||||||
|
@ -41,7 +41,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
* 其中:
|
* 其中:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* <strong>TaskLauncher</strong>:定时器每分钟调用一次(如果{@link Scheduler#isMatchSecond()}为<code>true</code>每秒调用一次),
|
* <strong>TaskLauncher</strong>:定时器每分钟调用一次(如果{@link Scheduler#isMatchSecond()}为{@code true}每秒调用一次),
|
||||||
* 负责检查<strong>TaskTable</strong>是否有匹配到此时间运行的Task
|
* 负责检查<strong>TaskTable</strong>是否有匹配到此时间运行的Task
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
@ -102,7 +102,7 @@ public class Scheduler implements Serializable {
|
|||||||
* 设置是否为守护线程<br>
|
* 设置是否为守护线程<br>
|
||||||
* 如果为true,则在调用{@link #stop()}方法后执行的定时任务立即结束,否则等待执行完毕才结束。默认非守护线程
|
* 如果为true,则在调用{@link #stop()}方法后执行的定时任务立即结束,否则等待执行完毕才结束。默认非守护线程
|
||||||
*
|
*
|
||||||
* @param on <code>true</code>为守护线程,否则非守护线程
|
* @param on {@code true}为守护线程,否则非守护线程
|
||||||
* @return this
|
* @return this
|
||||||
* @throws CronException 定时任务已经启动抛出此异常
|
* @throws CronException 定时任务已经启动抛出此异常
|
||||||
*/
|
*/
|
||||||
@ -131,7 +131,7 @@ public class Scheduler implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* 是否支持秒匹配
|
* 是否支持秒匹配
|
||||||
*
|
*
|
||||||
* @return <code>true</code>使用,<code>false</code>不使用
|
* @return {@code true}使用,{@code false}不使用
|
||||||
*/
|
*/
|
||||||
public boolean isMatchSecond() {
|
public boolean isMatchSecond() {
|
||||||
return this.config.isMatchSecond();
|
return this.config.isMatchSecond();
|
||||||
@ -140,7 +140,7 @@ public class Scheduler implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* 设置是否支持秒匹配,默认不使用
|
* 设置是否支持秒匹配,默认不使用
|
||||||
*
|
*
|
||||||
* @param isMatchSecond <code>true</code>支持,<code>false</code>不支持
|
* @param isMatchSecond {@code true}支持,{@code false}不支持
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public Scheduler setMatchSecond(boolean isMatchSecond) {
|
public Scheduler setMatchSecond(boolean isMatchSecond) {
|
||||||
@ -380,7 +380,7 @@ public class Scheduler implements Serializable {
|
|||||||
throw new CronException("Schedule is started!");
|
throw new CronException("Schedule is started!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 无界线程池,确保每一个需要执行的线程都可以及时运行,同时复用已有现成避免线程重复创建
|
// 无界线程池,确保每一个需要执行的线程都可以及时运行,同时复用已有线程避免线程重复创建
|
||||||
this.threadExecutor = ExecutorBuilder.create().useSynchronousQueue().setThreadFactory(//
|
this.threadExecutor = ExecutorBuilder.create().useSynchronousQueue().setThreadFactory(//
|
||||||
ThreadFactoryBuilder.create().setNamePrefix("hutool-cron-").setDaemon(this.daemon).build()//
|
ThreadFactoryBuilder.create().setNamePrefix("hutool-cron-").setDaemon(this.daemon).build()//
|
||||||
).build();
|
).build();
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-crypto</artifactId>
|
<artifactId>hutool-crypto</artifactId>
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package cn.hutool.crypto;
|
package cn.hutool.crypto;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
|
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||||
|
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||||
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
|
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
|
||||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||||
@ -13,6 +16,7 @@ import org.bouncycastle.jce.spec.ECNamedCurveSpec;
|
|||||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||||
import org.bouncycastle.math.ec.ECCurve;
|
import org.bouncycastle.math.ec.ECCurve;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
@ -41,15 +45,28 @@ public class BCUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编码压缩EC公钥(基于BouncyCastle)<br>
|
* 编码压缩EC公钥(基于BouncyCastle),即Q值<br>
|
||||||
* 见:https://www.cnblogs.com/xinzhao/p/8963724.html
|
* 见:https://www.cnblogs.com/xinzhao/p/8963724.html
|
||||||
*
|
*
|
||||||
* @param publicKey {@link PublicKey},必须为org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
|
* @param publicKey {@link PublicKey},必须为org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
|
||||||
* @return 压缩得到的X
|
* @return 压缩得到的Q
|
||||||
* @since 4.4.4
|
* @since 4.4.4
|
||||||
*/
|
*/
|
||||||
public static byte[] encodeECPublicKey(PublicKey publicKey) {
|
public static byte[] encodeECPublicKey(PublicKey publicKey) {
|
||||||
return ((BCECPublicKey) publicKey).getQ().getEncoded(true);
|
return encodeECPublicKey(publicKey, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编码压缩EC公钥(基于BouncyCastle),即Q值<br>
|
||||||
|
* 见:https://www.cnblogs.com/xinzhao/p/8963724.html
|
||||||
|
*
|
||||||
|
* @param publicKey {@link PublicKey},必须为org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
|
||||||
|
* @param isCompressed 是否压缩
|
||||||
|
* @return 得到的Q
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static byte[] encodeECPublicKey(PublicKey publicKey, boolean isCompressed) {
|
||||||
|
return ((BCECPublicKey) publicKey).getQ().getEncoded(isCompressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -300,4 +317,37 @@ public class BCUtil {
|
|||||||
public static PublicKey readPemPublicKey(InputStream pemStream) {
|
public static PublicKey readPemPublicKey(InputStream pemStream) {
|
||||||
return PemUtil.readPemPublicKey(pemStream);
|
return PemUtil.readPemPublicKey(pemStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java中的PKCS#8格式私钥转换为OpenSSL支持的PKCS#1格式
|
||||||
|
*
|
||||||
|
* @param privateKey PKCS#8格式私钥
|
||||||
|
* @return PKCS#1格式私钥
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static byte[] toPkcs1(PrivateKey privateKey){
|
||||||
|
final PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(privateKey.getEncoded());
|
||||||
|
try {
|
||||||
|
return pkInfo.parsePrivateKey().toASN1Primitive().getEncoded();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IORuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java中的X.509格式公钥转换为OpenSSL支持的PKCS#1格式
|
||||||
|
*
|
||||||
|
* @param publicKey X.509格式公钥
|
||||||
|
* @return PKCS#1格式公钥
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static byte[] toPkcs1(PublicKey publicKey){
|
||||||
|
final SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo
|
||||||
|
.getInstance(publicKey.getEncoded());
|
||||||
|
try {
|
||||||
|
return spkInfo.parsePublicKey().getEncoded();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IORuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,30 @@
|
|||||||
package cn.hutool.crypto;
|
package cn.hutool.crypto;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
|
import org.bouncycastle.asn1.ASN1Encoding;
|
||||||
|
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||||
|
import org.bouncycastle.asn1.sec.ECPrivateKey;
|
||||||
|
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||||
|
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
|
||||||
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
|
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
|
||||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||||
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
|
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
|
||||||
|
import org.bouncycastle.jcajce.spec.OpenSSHPrivateKeySpec;
|
||||||
|
import org.bouncycastle.jcajce.spec.OpenSSHPublicKeySpec;
|
||||||
import org.bouncycastle.math.ec.ECCurve;
|
import org.bouncycastle.math.ec.ECCurve;
|
||||||
|
import org.bouncycastle.math.ec.ECPoint;
|
||||||
|
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
|
||||||
import org.bouncycastle.util.BigIntegers;
|
import org.bouncycastle.util.BigIntegers;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
import java.security.spec.KeySpec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EC密钥参数相关工具类封装
|
* EC密钥参数相关工具类封装
|
||||||
@ -38,7 +50,21 @@ public class ECKeyUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据私钥参数获取公钥参数
|
||||||
|
*
|
||||||
|
* @param privateKeyParameters 私钥参数
|
||||||
|
* @return 公钥参数
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static ECPublicKeyParameters getPublicParams(ECPrivateKeyParameters privateKeyParameters) {
|
||||||
|
final ECDomainParameters domainParameters = privateKeyParameters.getParameters();
|
||||||
|
final ECPoint q = new FixedPointCombMultiplier().multiply(domainParameters.getG(), privateKeyParameters.getD());
|
||||||
|
return new ECPublicKeyParameters(q, domainParameters);
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------- Public Key
|
//--------------------------------------------------------------------------- Public Key
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换为 ECPublicKeyParameters
|
* 转换为 ECPublicKeyParameters
|
||||||
*
|
*
|
||||||
@ -87,7 +113,7 @@ public class ECKeyUtil {
|
|||||||
* @param x 公钥X
|
* @param x 公钥X
|
||||||
* @param y 公钥Y
|
* @param y 公钥Y
|
||||||
* @param domainParameters ECDomainParameters
|
* @param domainParameters ECDomainParameters
|
||||||
* @return ECPublicKeyParameters
|
* @return ECPublicKeyParameters,x或y为{@code null}则返回{@code null}
|
||||||
*/
|
*/
|
||||||
public static ECPublicKeyParameters toPublicParams(String x, String y, ECDomainParameters domainParameters) {
|
public static ECPublicKeyParameters toPublicParams(String x, String y, ECDomainParameters domainParameters) {
|
||||||
return toPublicParams(SecureUtil.decode(x), SecureUtil.decode(y), domainParameters);
|
return toPublicParams(SecureUtil.decode(x), SecureUtil.decode(y), domainParameters);
|
||||||
@ -102,6 +128,9 @@ public class ECKeyUtil {
|
|||||||
* @return ECPublicKeyParameters
|
* @return ECPublicKeyParameters
|
||||||
*/
|
*/
|
||||||
public static ECPublicKeyParameters toPublicParams(byte[] xBytes, byte[] yBytes, ECDomainParameters domainParameters) {
|
public static ECPublicKeyParameters toPublicParams(byte[] xBytes, byte[] yBytes, ECDomainParameters domainParameters) {
|
||||||
|
if (null == xBytes || null == yBytes) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return toPublicParams(BigIntegers.fromUnsignedByteArray(xBytes), BigIntegers.fromUnsignedByteArray(yBytes), domainParameters);
|
return toPublicParams(BigIntegers.fromUnsignedByteArray(xBytes), BigIntegers.fromUnsignedByteArray(yBytes), domainParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,6 +206,7 @@ public class ECKeyUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------- Private Key
|
//--------------------------------------------------------------------------- Private Key
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换为 ECPrivateKeyParameters
|
* 转换为 ECPrivateKeyParameters
|
||||||
*
|
*
|
||||||
@ -259,4 +289,108 @@ public class ECKeyUtil {
|
|||||||
throw new CryptoException(e);
|
throw new CryptoException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将SM2算法的{@link ECPrivateKey} 转换为 {@link PrivateKey}
|
||||||
|
*
|
||||||
|
* @param privateKey {@link ECPrivateKey}
|
||||||
|
* @return {@link PrivateKey}
|
||||||
|
*/
|
||||||
|
public static PrivateKey toSm2PrivateKey(ECPrivateKey privateKey) {
|
||||||
|
try {
|
||||||
|
final PrivateKeyInfo info = new PrivateKeyInfo(
|
||||||
|
new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SmUtil.ID_SM2_PUBLIC_KEY_PARAM), privateKey);
|
||||||
|
return KeyUtil.generatePrivateKey("SM2", info.getEncoded(ASN1Encoding.DER));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IORuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建{@link OpenSSHPrivateKeySpec}
|
||||||
|
*
|
||||||
|
* @param key 私钥,需为PKCS#1格式
|
||||||
|
* @return {@link OpenSSHPrivateKeySpec}
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static KeySpec createOpenSSHPrivateKeySpec(byte[] key) {
|
||||||
|
return new OpenSSHPrivateKeySpec(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建{@link OpenSSHPublicKeySpec}
|
||||||
|
*
|
||||||
|
* @param key 公钥,需为PKCS#1格式
|
||||||
|
* @return {@link OpenSSHPublicKeySpec}
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static KeySpec createOpenSSHPublicKeySpec(byte[] key) {
|
||||||
|
return new OpenSSHPublicKeySpec(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试解析转换各种类型私钥为{@link ECPrivateKeyParameters},支持包括:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>D值</li>
|
||||||
|
* <li>PKCS#8</li>
|
||||||
|
* <li>PKCS#1</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param privateKeyBytes 私钥
|
||||||
|
* @return {@link ECPrivateKeyParameters}
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static ECPrivateKeyParameters decodePrivateKeyParams(byte[] privateKeyBytes) {
|
||||||
|
try {
|
||||||
|
// 尝试D值
|
||||||
|
return toSm2PrivateParams(privateKeyBytes);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
PrivateKey privateKey;
|
||||||
|
//尝试PKCS#8
|
||||||
|
try {
|
||||||
|
privateKey = KeyUtil.generatePrivateKey("sm2", privateKeyBytes);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// 尝试PKCS#1
|
||||||
|
privateKey = KeyUtil.generatePrivateKey("sm2", createOpenSSHPrivateKeySpec(privateKeyBytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
return toPrivateParams(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试解析转换各种类型公钥为{@link ECPublicKeyParameters},支持包括:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Q值</li>
|
||||||
|
* <li>X.509</li>
|
||||||
|
* <li>PKCS#1</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param publicKeyBytes 公钥
|
||||||
|
* @return {@link ECPublicKeyParameters}
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static ECPublicKeyParameters decodePublicKeyParams(byte[] publicKeyBytes) {
|
||||||
|
try {
|
||||||
|
// 尝试Q值
|
||||||
|
return toSm2PublicParams(publicKeyBytes);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicKey publicKey;
|
||||||
|
//尝试X.509
|
||||||
|
try {
|
||||||
|
publicKey = KeyUtil.generatePublicKey("sm2", publicKeyBytes);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// 尝试PKCS#1
|
||||||
|
publicKey = KeyUtil.generatePublicKey("sm2", createOpenSSHPublicKeySpec(publicKeyBytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
return toPublicParams(publicKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,8 +256,8 @@ public class KeyUtil {
|
|||||||
* 采用PKCS#8规范,此规范定义了私钥信息语法和加密私钥语法<br>
|
* 采用PKCS#8规范,此规范定义了私钥信息语法和加密私钥语法<br>
|
||||||
* 算法见:https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyFactory
|
* 算法见:https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyFactory
|
||||||
*
|
*
|
||||||
* @param algorithm 算法
|
* @param algorithm 算法,如RSA、EC、SM2等
|
||||||
* @param key 密钥,必须为DER编码存储
|
* @param key 密钥,PKCS#8格式
|
||||||
* @return 私钥 {@link PrivateKey}
|
* @return 私钥 {@link PrivateKey}
|
||||||
*/
|
*/
|
||||||
public static PrivateKey generatePrivateKey(String algorithm, byte[] key) {
|
public static PrivateKey generatePrivateKey(String algorithm, byte[] key) {
|
||||||
@ -271,7 +271,7 @@ public class KeyUtil {
|
|||||||
* 生成私钥,仅用于非对称加密<br>
|
* 生成私钥,仅用于非对称加密<br>
|
||||||
* 算法见:https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyFactory
|
* 算法见:https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyFactory
|
||||||
*
|
*
|
||||||
* @param algorithm 算法
|
* @param algorithm 算法,如RSA、EC、SM2等
|
||||||
* @param keySpec {@link KeySpec}
|
* @param keySpec {@link KeySpec}
|
||||||
* @return 私钥 {@link PrivateKey}
|
* @return 私钥 {@link PrivateKey}
|
||||||
* @since 3.1.1
|
* @since 3.1.1
|
||||||
|
@ -12,6 +12,8 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.io.Writer;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
@ -66,7 +68,8 @@ public class PemUtil {
|
|||||||
//private
|
//private
|
||||||
if (type.endsWith("EC PRIVATE KEY")) {
|
if (type.endsWith("EC PRIVATE KEY")) {
|
||||||
return KeyUtil.generatePrivateKey("EC", object.getContent());
|
return KeyUtil.generatePrivateKey("EC", object.getContent());
|
||||||
}if (type.endsWith("PRIVATE KEY")) {
|
}
|
||||||
|
if (type.endsWith("PRIVATE KEY")) {
|
||||||
return KeyUtil.generateRSAPrivateKey(object.getContent());
|
return KeyUtil.generateRSAPrivateKey(object.getContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,11 +132,38 @@ public class PemUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取OpenSSL生成的ANS1格式的Pem私钥文件,必须为PKCS#1格式
|
||||||
|
*
|
||||||
|
* @param keyStream 私钥pem流
|
||||||
|
* @return {@link PrivateKey}
|
||||||
|
*/
|
||||||
|
public static PrivateKey readSm2PemPrivateKey(InputStream keyStream) {
|
||||||
|
try{
|
||||||
|
return KeyUtil.generatePrivateKey("sm2", ECKeyUtil.createOpenSSHPrivateKeySpec(readPem(keyStream)));
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(keyStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将私钥或公钥转换为PEM格式的字符串
|
||||||
|
* @param type 密钥类型(私钥、公钥、证书)
|
||||||
|
* @param content 密钥内容
|
||||||
|
* @return PEM内容
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static String toPem(String type, byte[] content) {
|
||||||
|
final StringWriter stringWriter = new StringWriter();
|
||||||
|
writePemObject(type, content, stringWriter);
|
||||||
|
return stringWriter.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 写出pem密钥(私钥、公钥、证书)
|
* 写出pem密钥(私钥、公钥、证书)
|
||||||
*
|
*
|
||||||
* @param type 密钥类型(私钥、公钥、证书)
|
* @param type 密钥类型(私钥、公钥、证书)
|
||||||
* @param content 密钥内容
|
* @param content 密钥内容,需为PKCS#1格式
|
||||||
* @param keyStream pem流
|
* @param keyStream pem流
|
||||||
* @since 5.1.6
|
* @since 5.1.6
|
||||||
*/
|
*/
|
||||||
@ -141,6 +171,18 @@ public class PemUtil {
|
|||||||
writePemObject(new PemObject(type, content), keyStream);
|
writePemObject(new PemObject(type, content), keyStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写出pem密钥(私钥、公钥、证书)
|
||||||
|
*
|
||||||
|
* @param type 密钥类型(私钥、公钥、证书)
|
||||||
|
* @param content 密钥内容,需为PKCS#1格式
|
||||||
|
* @param writer pemWriter
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static void writePemObject(String type, byte[] content, Writer writer) {
|
||||||
|
writePemObject(new PemObject(type, content), writer);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 写出pem密钥(私钥、公钥、证书)
|
* 写出pem密钥(私钥、公钥、证书)
|
||||||
*
|
*
|
||||||
@ -149,14 +191,24 @@ public class PemUtil {
|
|||||||
* @since 5.1.6
|
* @since 5.1.6
|
||||||
*/
|
*/
|
||||||
public static void writePemObject(PemObjectGenerator pemObject, OutputStream keyStream) {
|
public static void writePemObject(PemObjectGenerator pemObject, OutputStream keyStream) {
|
||||||
PemWriter writer = null;
|
writePemObject(pemObject, IoUtil.getUtf8Writer(keyStream));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写出pem密钥(私钥、公钥、证书)
|
||||||
|
*
|
||||||
|
* @param pemObject pem对象,包括密钥和密钥类型等信息
|
||||||
|
* @param writer pemWriter
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static void writePemObject(PemObjectGenerator pemObject, Writer writer) {
|
||||||
|
final PemWriter pemWriter = new PemWriter(writer);
|
||||||
try {
|
try {
|
||||||
writer = new PemWriter(IoUtil.getUtf8Writer(keyStream));
|
pemWriter.writeObject(pemObject);
|
||||||
writer.writeObject(pemObject);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
} finally {
|
} finally {
|
||||||
IoUtil.close(writer);
|
IoUtil.close(pemWriter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import cn.hutool.crypto.digest.MD5;
|
|||||||
import cn.hutool.crypto.symmetric.AES;
|
import cn.hutool.crypto.symmetric.AES;
|
||||||
import cn.hutool.crypto.symmetric.DES;
|
import cn.hutool.crypto.symmetric.DES;
|
||||||
import cn.hutool.crypto.symmetric.DESede;
|
import cn.hutool.crypto.symmetric.DESede;
|
||||||
|
import cn.hutool.crypto.symmetric.PBKDF2;
|
||||||
import cn.hutool.crypto.symmetric.RC4;
|
import cn.hutool.crypto.symmetric.RC4;
|
||||||
import cn.hutool.crypto.symmetric.SymmetricCrypto;
|
import cn.hutool.crypto.symmetric.SymmetricCrypto;
|
||||||
|
|
||||||
@ -1048,4 +1049,16 @@ public final class SecureUtil {
|
|||||||
public static void disableBouncyCastle() {
|
public static void disableBouncyCastle() {
|
||||||
GlobalBouncyCastleProvider.setUseBouncyCastle(false);
|
GlobalBouncyCastleProvider.setUseBouncyCastle(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PBKDF2加密密码
|
||||||
|
*
|
||||||
|
* @param password 密码
|
||||||
|
* @param salt 盐
|
||||||
|
* @return 盐,一般为16位
|
||||||
|
* @since 5.6.0
|
||||||
|
*/
|
||||||
|
public static String pbkdf2(char[] password, byte[] salt){
|
||||||
|
return new PBKDF2().encryptHex(password, salt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package cn.hutool.crypto;
|
package cn.hutool.crypto;
|
||||||
|
|
||||||
import cn.hutool.core.io.IORuntimeException;
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.crypto.asymmetric.SM2;
|
import cn.hutool.crypto.asymmetric.SM2;
|
||||||
import cn.hutool.crypto.digest.HMac;
|
import cn.hutool.crypto.digest.HMac;
|
||||||
import cn.hutool.crypto.digest.HmacAlgorithm;
|
import cn.hutool.crypto.digest.HmacAlgorithm;
|
||||||
@ -9,13 +10,13 @@ import cn.hutool.crypto.digest.mac.BCHMacEngine;
|
|||||||
import cn.hutool.crypto.digest.mac.MacEngine;
|
import cn.hutool.crypto.digest.mac.MacEngine;
|
||||||
import cn.hutool.crypto.symmetric.SM4;
|
import cn.hutool.crypto.symmetric.SM4;
|
||||||
import cn.hutool.crypto.symmetric.SymmetricCrypto;
|
import cn.hutool.crypto.symmetric.SymmetricCrypto;
|
||||||
import org.bouncycastle.asn1.ASN1EncodableVector;
|
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||||
import org.bouncycastle.asn1.ASN1Integer;
|
|
||||||
import org.bouncycastle.asn1.ASN1Sequence;
|
|
||||||
import org.bouncycastle.asn1.DERSequence;
|
|
||||||
import org.bouncycastle.asn1.gm.GMNamedCurves;
|
import org.bouncycastle.asn1.gm.GMNamedCurves;
|
||||||
import org.bouncycastle.crypto.digests.SM3Digest;
|
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||||
|
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||||
|
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||||
|
import org.bouncycastle.crypto.signers.StandardDSAEncoding;
|
||||||
import org.bouncycastle.util.Arrays;
|
import org.bouncycastle.util.Arrays;
|
||||||
import org.bouncycastle.util.encoders.Hex;
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
|
|
||||||
@ -23,16 +24,26 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SM国密算法工具类<br>
|
* SM国密算法工具类<br>
|
||||||
* 此工具类依赖org.bouncycastle:bcpkix-jdk15on
|
* 此工具类依赖org.bouncycastle:bcprov-jdk15to18
|
||||||
|
*
|
||||||
|
* <p>封装包括:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>SM2 椭圆曲线非对称加密和签名</li>
|
||||||
|
* <li>SM3 杂凑算法</li>
|
||||||
|
* <li>SM4 对称加密</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author looly
|
* @author looly
|
||||||
* @since 4.3.2
|
* @since 4.3.2
|
||||||
*/
|
*/
|
||||||
public class SmUtil {
|
public class SmUtil {
|
||||||
|
|
||||||
|
private final static int RS_LEN = 32;
|
||||||
/**
|
/**
|
||||||
* SM2默认曲线
|
* SM2默认曲线
|
||||||
*/
|
*/
|
||||||
@ -40,13 +51,11 @@ public class SmUtil {
|
|||||||
/**
|
/**
|
||||||
* SM2推荐曲线参数(来自https://github.com/ZZMarquis/gmhelper)
|
* SM2推荐曲线参数(来自https://github.com/ZZMarquis/gmhelper)
|
||||||
*/
|
*/
|
||||||
public static final ECDomainParameters SM2_DOMAIN_PARAMS;
|
public static final ECDomainParameters SM2_DOMAIN_PARAMS = BCUtil.toDomainParams(GMNamedCurves.getByName(SM2_CURVE_NAME));
|
||||||
|
/**
|
||||||
private final static int RS_LEN = 32;
|
* SM2国密算法公钥参数的Oid标识
|
||||||
|
*/
|
||||||
static {
|
public static final ASN1ObjectIdentifier ID_SM2_PUBLIC_KEY_PARAM = new ASN1ObjectIdentifier("1.2.156.10197.1.301");
|
||||||
SM2_DOMAIN_PARAMS = BCUtil.toDomainParams(GMNamedCurves.getByName(SM2_CURVE_NAME));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建SM2算法对象<br>
|
* 创建SM2算法对象<br>
|
||||||
@ -76,14 +85,42 @@ public class SmUtil {
|
|||||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||||
*
|
*
|
||||||
* @param privateKey 私钥
|
* @param privateKey 私钥,必须使用PKCS#8规范
|
||||||
* @param publicKey 公钥
|
* @param publicKey 公钥,必须使用X509规范
|
||||||
* @return {@link SM2}
|
* @return {@link SM2}
|
||||||
*/
|
*/
|
||||||
public static SM2 sm2(byte[] privateKey, byte[] publicKey) {
|
public static SM2 sm2(byte[] privateKey, byte[] publicKey) {
|
||||||
return new SM2(privateKey, publicKey);
|
return new SM2(privateKey, publicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建SM2算法对象<br>
|
||||||
|
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||||
|
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||||
|
*
|
||||||
|
* @param privateKey 私钥
|
||||||
|
* @param publicKey 公钥
|
||||||
|
* @return {@link SM2}
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static SM2 sm2(PrivateKey privateKey, PublicKey publicKey) {
|
||||||
|
return new SM2(privateKey, publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建SM2算法对象<br>
|
||||||
|
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||||
|
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||||
|
*
|
||||||
|
* @param privateKeyParams 私钥参数
|
||||||
|
* @param publicKeyParams 公钥参数
|
||||||
|
* @return {@link SM2}
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public static SM2 sm2(ECPrivateKeyParameters privateKeyParams, ECPublicKeyParameters publicKeyParams) {
|
||||||
|
return new SM2(privateKeyParams, publicKeyParams);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SM3加密<br>
|
* SM3加密<br>
|
||||||
* 例:<br>
|
* 例:<br>
|
||||||
@ -194,27 +231,28 @@ public class SmUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BC的SM3withSM2签名得到的结果的rs是asn1格式的,这个方法转化成直接拼接r||s<br>
|
* BC的SM3withSM2签名得到的结果的rs是asn1格式的,这个方法转化成直接拼接r||s
|
||||||
* 来自:https://blog.csdn.net/pridas/article/details/86118774
|
|
||||||
*
|
*
|
||||||
* @param rsDer rs in asn1 format
|
* @param rsDer rs in asn1 format
|
||||||
* @return sign result in plain byte array
|
* @return sign result in plain byte array
|
||||||
* @since 4.5.0
|
* @since 4.5.0
|
||||||
*/
|
*/
|
||||||
public static byte[] rsAsn1ToPlain(byte[] rsDer) {
|
public static byte[] rsAsn1ToPlain(byte[] rsDer) {
|
||||||
ASN1Sequence seq = ASN1Sequence.getInstance(rsDer);
|
final BigInteger[] decode;
|
||||||
byte[] r = bigIntToFixedLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(0)).getValue());
|
try {
|
||||||
byte[] s = bigIntToFixedLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(1)).getValue());
|
decode = StandardDSAEncoding.INSTANCE.decode(SM2_DOMAIN_PARAMS.getN(), rsDer);
|
||||||
byte[] result = new byte[RS_LEN * 2];
|
} catch (IOException e) {
|
||||||
System.arraycopy(r, 0, result, 0, r.length);
|
throw new IORuntimeException(e);
|
||||||
System.arraycopy(s, 0, result, RS_LEN, s.length);
|
}
|
||||||
|
|
||||||
return result;
|
final byte[] r = bigIntToFixedLengthBytes(decode[0]);
|
||||||
|
final byte[] s = bigIntToFixedLengthBytes(decode[1]);
|
||||||
|
|
||||||
|
return ArrayUtil.addAll(r, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BC的SM3withSM2验签需要的rs是asn1格式的,这个方法将直接拼接r||s的字节数组转化成asn1格式<br>
|
* BC的SM3withSM2验签需要的rs是asn1格式的,这个方法将直接拼接r||s的字节数组转化成asn1格式
|
||||||
* 来自:https://blog.csdn.net/pridas/article/details/86118774
|
|
||||||
*
|
*
|
||||||
* @param sign in plain byte array
|
* @param sign in plain byte array
|
||||||
* @return rs result in asn1 format
|
* @return rs result in asn1 format
|
||||||
@ -226,11 +264,8 @@ public class SmUtil {
|
|||||||
}
|
}
|
||||||
BigInteger r = new BigInteger(1, Arrays.copyOfRange(sign, 0, RS_LEN));
|
BigInteger r = new BigInteger(1, Arrays.copyOfRange(sign, 0, RS_LEN));
|
||||||
BigInteger s = new BigInteger(1, Arrays.copyOfRange(sign, RS_LEN, RS_LEN * 2));
|
BigInteger s = new BigInteger(1, Arrays.copyOfRange(sign, RS_LEN, RS_LEN * 2));
|
||||||
ASN1EncodableVector v = new ASN1EncodableVector();
|
|
||||||
v.add(new ASN1Integer(r));
|
|
||||||
v.add(new ASN1Integer(s));
|
|
||||||
try {
|
try {
|
||||||
return new DERSequence(v).getEncoded("DER");
|
return StandardDSAEncoding.INSTANCE.encode(SM2_DOMAIN_PARAMS.getN(), r, s);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import cn.hutool.core.lang.Assert;
|
|||||||
import cn.hutool.core.util.HexUtil;
|
import cn.hutool.core.util.HexUtil;
|
||||||
import cn.hutool.crypto.BCUtil;
|
import cn.hutool.crypto.BCUtil;
|
||||||
import cn.hutool.crypto.CryptoException;
|
import cn.hutool.crypto.CryptoException;
|
||||||
import cn.hutool.crypto.KeyUtil;
|
import cn.hutool.crypto.ECKeyUtil;
|
||||||
import cn.hutool.crypto.SecureUtil;
|
import cn.hutool.crypto.SecureUtil;
|
||||||
import org.bouncycastle.crypto.CipherParameters;
|
import org.bouncycastle.crypto.CipherParameters;
|
||||||
import org.bouncycastle.crypto.Digest;
|
import org.bouncycastle.crypto.Digest;
|
||||||
@ -62,8 +62,8 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
|||||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||||
*
|
*
|
||||||
* @param privateKeyStr 私钥Hex或Base64表示
|
* @param privateKeyStr 私钥Hex或Base64表示,必须使用PKCS#8规范
|
||||||
* @param publicKeyStr 公钥Hex或Base64表示
|
* @param publicKeyStr 公钥Hex或Base64表示,必须使用X509规范
|
||||||
*/
|
*/
|
||||||
public SM2(String privateKeyStr, String publicKeyStr) {
|
public SM2(String privateKeyStr, String publicKeyStr) {
|
||||||
this(SecureUtil.decode(privateKeyStr), SecureUtil.decode(publicKeyStr));
|
this(SecureUtil.decode(privateKeyStr), SecureUtil.decode(publicKeyStr));
|
||||||
@ -74,13 +74,13 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
|||||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||||
*
|
*
|
||||||
* @param privateKey 私钥
|
* @param privateKey 私钥,可以使用PKCS#8、D值或PKCS#1规范
|
||||||
* @param publicKey 公钥
|
* @param publicKey 公钥,可以使用X509、Q值或PKCS#1规范
|
||||||
*/
|
*/
|
||||||
public SM2(byte[] privateKey, byte[] publicKey) {
|
public SM2(byte[] privateKey, byte[] publicKey) {
|
||||||
this(//
|
this(
|
||||||
KeyUtil.generatePrivateKey(ALGORITHM_SM2, privateKey), //
|
ECKeyUtil.decodePrivateKeyParams(privateKey),
|
||||||
KeyUtil.generatePublicKey(ALGORITHM_SM2, publicKey)//
|
ECKeyUtil.decodePublicKeyParams(publicKey)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,8 +135,8 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
|||||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||||
*
|
*
|
||||||
* @param privateKeyParams 私钥
|
* @param privateKeyParams 私钥,可以为null
|
||||||
* @param publicKeyParams 公钥
|
* @param publicKeyParams 公钥,可以为null
|
||||||
*/
|
*/
|
||||||
public SM2(ECPrivateKeyParameters privateKeyParams, ECPublicKeyParameters publicKeyParams) {
|
public SM2(ECPrivateKeyParameters privateKeyParams, ECPublicKeyParameters publicKeyParams) {
|
||||||
super(ALGORITHM_SM2, null, null);
|
super(ALGORITHM_SM2, null, null);
|
||||||
@ -274,7 +274,8 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用私钥对信息生成数字签名
|
* 用私钥对信息生成数字签名,签名格式为ASN1<br>
|
||||||
|
* * 在硬件签名中,返回结果为R+S,可以通过调用{@link cn.hutool.crypto.SmUtil#rsAsn1ToPlain(byte[])}方法转换之。
|
||||||
*
|
*
|
||||||
* @param data 加密数据
|
* @param data 加密数据
|
||||||
* @return 签名
|
* @return 签名
|
||||||
@ -295,7 +296,8 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用私钥对信息生成数字签名
|
* 用私钥对信息生成数字签名,签名格式为ASN1<br>
|
||||||
|
* 在硬件签名中,返回结果为R+S,可以通过调用{@link cn.hutool.crypto.SmUtil#rsAsn1ToPlain(byte[])}方法转换之。
|
||||||
*
|
*
|
||||||
* @param data 被签名的数据数据
|
* @param data 被签名的数据数据
|
||||||
* @param id 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
|
* @param id 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
|
||||||
@ -471,6 +473,27 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得私钥D值(编码后的私钥)
|
||||||
|
*
|
||||||
|
* @return D值
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public byte[] getD() {
|
||||||
|
return this.privateKeyParams.getD().toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得公钥Q值(编码后的公钥)
|
||||||
|
*
|
||||||
|
* @param isCompressed 是否压缩
|
||||||
|
* @return Q值
|
||||||
|
* @since 5.5.9
|
||||||
|
*/
|
||||||
|
public byte[] getQ(boolean isCompressed) {
|
||||||
|
return this.publicKeyParams.getQ().getEncoded(isCompressed);
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------------------------------- Private method start
|
// ------------------------------------------------------------------------------------------------------------------------- Private method start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package cn.hutool.crypto.digest;
|
package cn.hutool.crypto.digest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SM3算法
|
* SM3杂凑算法
|
||||||
*
|
*
|
||||||
* @author looly
|
* @author looly
|
||||||
* @since 4.6.8
|
* @since 4.6.8
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
package cn.hutool.crypto.symmetric;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.HexUtil;
|
||||||
|
import cn.hutool.crypto.KeyUtil;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.PBEKeySpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PBKDF2应用一个伪随机函数以导出密钥,PBKDF2简单而言就是将salted hash进行多次重复计算。
|
||||||
|
* 参考:https://blog.csdn.net/huoji555/article/details/83659687
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
public class PBKDF2 {
|
||||||
|
|
||||||
|
private String algorithm = "PBKDF2WithHmacSHA1";
|
||||||
|
//生成密文的长度
|
||||||
|
private int keyLength = 512;
|
||||||
|
|
||||||
|
//迭代次数
|
||||||
|
private int iterationCount = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造,算法PBKDF2WithHmacSHA1,盐长度16,密文长度512,迭代次数1000
|
||||||
|
*/
|
||||||
|
public PBKDF2() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param algorithm 算法,一般为PBKDF2WithXXX
|
||||||
|
* @param keyLength 生成密钥长度,默认512
|
||||||
|
* @param iterationCount 迭代次数,默认1000
|
||||||
|
*/
|
||||||
|
public PBKDF2(String algorithm, int keyLength, int iterationCount) {
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
this.keyLength = keyLength;
|
||||||
|
this.iterationCount = iterationCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*
|
||||||
|
* @param password 密码
|
||||||
|
* @param salt 盐
|
||||||
|
* @return 加密后的密码
|
||||||
|
*/
|
||||||
|
public byte[] encrypt(char[] password, byte[] salt) {
|
||||||
|
final PBEKeySpec pbeKeySpec = new PBEKeySpec(password, salt, iterationCount, keyLength);
|
||||||
|
final SecretKey secretKey = KeyUtil.generateKey(algorithm, pbeKeySpec);
|
||||||
|
return secretKey.getEncoded();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*
|
||||||
|
* @param password 密码
|
||||||
|
* @param salt 盐
|
||||||
|
* @return 加密后的密码
|
||||||
|
*/
|
||||||
|
public String encryptHex(char[] password, byte[] salt) {
|
||||||
|
return HexUtil.encodeHexStr(encrypt(password, salt));
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,17 @@
|
|||||||
package cn.hutool.crypto.test;
|
package cn.hutool.crypto.test;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.io.resource.ResourceUtil;
|
import cn.hutool.core.io.resource.ResourceUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.crypto.PemUtil;
|
import cn.hutool.crypto.PemUtil;
|
||||||
import cn.hutool.crypto.asymmetric.KeyType;
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
import cn.hutool.crypto.asymmetric.RSA;
|
import cn.hutool.crypto.asymmetric.RSA;
|
||||||
|
import cn.hutool.crypto.asymmetric.SM2;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
|
||||||
@ -45,7 +50,31 @@ public class PemUtilTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void readECPrivateKeyTest() {
|
public void readECPrivateKeyTest() {
|
||||||
PrivateKey privateKey = PemUtil.readPemPrivateKey(ResourceUtil.getStream("test_ec_private_key.pem"));
|
PrivateKey privateKey = PemUtil.readSm2PemPrivateKey(ResourceUtil.getStream("test_ec_private_key.pem"));
|
||||||
Assert.assertNotNull(privateKey);
|
SM2 sm2 = new SM2(privateKey, null);
|
||||||
|
sm2.usePlainEncoding();
|
||||||
|
|
||||||
|
//需要签名的明文,得到明文对应的字节数组
|
||||||
|
byte[] dataBytes = "我是一段测试aaaa".getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
byte[] sign = sm2.sign(dataBytes, null);
|
||||||
|
// 64位签名
|
||||||
|
Assert.assertEquals(64, sign.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void readECPrivateKeyTest2() {
|
||||||
|
// https://gitee.com/loolly/hutool/issues/I37Z75
|
||||||
|
byte[] d = PemUtil.readPem(FileUtil.getInputStream("d:/test/keys/priv.key"));
|
||||||
|
byte[] publicKey = PemUtil.readPem(FileUtil.getInputStream("d:/test/keys/pub.key"));
|
||||||
|
|
||||||
|
SM2 sm2 = new SM2(d, publicKey);
|
||||||
|
sm2.usePlainEncoding();
|
||||||
|
|
||||||
|
String content = "我是Hanley.";
|
||||||
|
byte[] sign = sm2.sign(StrUtil.utf8Bytes(content));
|
||||||
|
boolean verify = sm2.verify(StrUtil.utf8Bytes(content), sign);
|
||||||
|
Assert.assertTrue(verify);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package cn.hutool.crypto.test.asymmetric;
|
package cn.hutool.crypto.test.asymmetric;
|
||||||
|
|
||||||
import cn.hutool.core.codec.Base64;
|
import cn.hutool.core.codec.Base64;
|
||||||
import cn.hutool.core.lang.Console;
|
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
import cn.hutool.core.util.HexUtil;
|
import cn.hutool.core.util.HexUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
@ -13,10 +12,11 @@ import cn.hutool.crypto.asymmetric.KeyType;
|
|||||||
import cn.hutool.crypto.asymmetric.SM2;
|
import cn.hutool.crypto.asymmetric.SM2;
|
||||||
import org.bouncycastle.crypto.engines.SM2Engine;
|
import org.bouncycastle.crypto.engines.SM2Engine;
|
||||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
import org.bouncycastle.jcajce.spec.OpenSSHPrivateKeySpec;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
@ -31,7 +31,6 @@ public class SM2Test {
|
|||||||
@Test
|
@Test
|
||||||
public void generateKeyPairTest() {
|
public void generateKeyPairTest() {
|
||||||
KeyPair pair = SecureUtil.generateKeyPair("SM2");
|
KeyPair pair = SecureUtil.generateKeyPair("SM2");
|
||||||
Console.log(HexUtil.encodeHexStr(pair.getPublic().getEncoded()));
|
|
||||||
Assert.assertNotNull(pair.getPrivate());
|
Assert.assertNotNull(pair.getPrivate());
|
||||||
Assert.assertNotNull(pair.getPublic());
|
Assert.assertNotNull(pair.getPublic());
|
||||||
}
|
}
|
||||||
@ -114,14 +113,44 @@ public class SM2Test {
|
|||||||
Assert.assertEquals(text.toString(), decryptStr2);
|
Assert.assertEquals(text.toString(), decryptStr2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sm2SignTest(){
|
||||||
|
//需要签名的明文,得到明文对应的字节数组
|
||||||
|
byte[] dataBytes = "我是一段测试aaaa".getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
//指定的私钥
|
||||||
|
String privateKeyHex = "1ebf8b341c695ee456fd1a41b82645724bc25d79935437d30e7e4b0a554baa5e";
|
||||||
|
final SM2 sm2 = new SM2(privateKeyHex, null, null);
|
||||||
|
sm2.usePlainEncoding();
|
||||||
|
byte[] sign = sm2.sign(dataBytes, null);
|
||||||
|
// 64位签名
|
||||||
|
Assert.assertEquals(64, sign.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sm2VerifyTest(){
|
||||||
|
//指定的公钥
|
||||||
|
String publicKeyHex = "04db9629dd33ba568e9507add5df6587a0998361a03d3321948b448c653c2c1b7056434884ab6f3d1c529501f166a336e86f045cea10dffe58aa82ea13d7253763";
|
||||||
|
//需要加密的明文,得到明文对应的字节数组
|
||||||
|
byte[] dataBytes = "我是一段测试aaaa".getBytes(StandardCharsets.UTF_8);
|
||||||
|
//签名值
|
||||||
|
String signHex = "2881346e038d2ed706ccdd025f2b1dafa7377d5cf090134b98756fafe084dddbcdba0ab00b5348ed48025195af3f1dda29e819bb66aa9d4d088050ff148482a1";
|
||||||
|
|
||||||
|
final SM2 sm2 = new SM2(null, publicKeyHex);
|
||||||
|
sm2.usePlainEncoding();
|
||||||
|
|
||||||
|
boolean verify = sm2.verify(dataBytes, HexUtil.decodeHex(signHex));
|
||||||
|
Assert.assertTrue(verify);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sm2SignAndVerifyTest() {
|
public void sm2SignAndVerifyTest() {
|
||||||
String content = "我是Hanley.";
|
String content = "我是Hanley.";
|
||||||
|
|
||||||
final SM2 sm2 = SmUtil.sm2();
|
final SM2 sm2 = SmUtil.sm2();
|
||||||
|
|
||||||
byte[] sign = sm2.sign(content.getBytes());
|
byte[] sign = sm2.sign(StrUtil.utf8Bytes(content));
|
||||||
boolean verify = sm2.verify(content.getBytes(), sign);
|
boolean verify = sm2.verify(StrUtil.utf8Bytes(content), sign);
|
||||||
Assert.assertTrue(verify);
|
Assert.assertTrue(verify);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,8 +173,8 @@ public class SM2Test {
|
|||||||
|
|
||||||
final SM2 sm2 = new SM2(pair.getPrivate(), pair.getPublic());
|
final SM2 sm2 = new SM2(pair.getPrivate(), pair.getPublic());
|
||||||
|
|
||||||
byte[] sign = sm2.sign(content.getBytes());
|
byte[] sign = sm2.sign(content.getBytes(StandardCharsets.UTF_8));
|
||||||
boolean verify = sm2.verify(content.getBytes(), sign);
|
boolean verify = sm2.verify(content.getBytes(StandardCharsets.UTF_8), sign);
|
||||||
Assert.assertTrue(verify);
|
Assert.assertTrue(verify);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,8 +189,8 @@ public class SM2Test {
|
|||||||
HexUtil.encodeHexStr(pair.getPublic().getEncoded())//
|
HexUtil.encodeHexStr(pair.getPublic().getEncoded())//
|
||||||
);
|
);
|
||||||
|
|
||||||
byte[] sign = sm2.sign(content.getBytes());
|
byte[] sign = sm2.sign(content.getBytes(StandardCharsets.UTF_8));
|
||||||
boolean verify = sm2.verify(content.getBytes(), sign);
|
boolean verify = sm2.verify(content.getBytes(StandardCharsets.UTF_8), sign);
|
||||||
Assert.assertTrue(verify);
|
Assert.assertTrue(verify);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,8 +236,12 @@ public class SM2Test {
|
|||||||
// 生成的签名是64位
|
// 生成的签名是64位
|
||||||
sm2.usePlainEncoding();
|
sm2.usePlainEncoding();
|
||||||
|
|
||||||
|
|
||||||
String sign = "DCA0E80A7F46C93714B51C3EFC55A922BCEF7ECF0FE9E62B53BA6A7438B543A76C145A452CA9036F3CB70D7E6C67D4D9D7FE114E5367A2F6F5A4D39F2B10F3D6";
|
String sign = "DCA0E80A7F46C93714B51C3EFC55A922BCEF7ECF0FE9E62B53BA6A7438B543A76C145A452CA9036F3CB70D7E6C67D4D9D7FE114E5367A2F6F5A4D39F2B10F3D6";
|
||||||
Assert.assertTrue(sm2.verifyHex(data, sign));
|
Assert.assertTrue(sm2.verifyHex(data, sign));
|
||||||
|
|
||||||
|
String sign2 = sm2.signHex(data, id);
|
||||||
|
Assert.assertTrue(sm2.verifyHex(data, sign2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -218,13 +251,9 @@ public class SM2Test {
|
|||||||
|
|
||||||
String data = "123456";
|
String data = "123456";
|
||||||
|
|
||||||
final ECPublicKeyParameters ecPublicKeyParameters = ECKeyUtil.toSm2PublicParams(q);
|
final SM2 sm2 = new SM2(d, q);
|
||||||
final ECPrivateKeyParameters ecPrivateKeyParameters = ECKeyUtil.toSm2PrivateParams(d);
|
|
||||||
|
|
||||||
final SM2 sm2 = new SM2(ecPrivateKeyParameters, ecPublicKeyParameters);
|
|
||||||
sm2.setMode(SM2Engine.Mode.C1C2C3);
|
sm2.setMode(SM2Engine.Mode.C1C2C3);
|
||||||
final String encryptHex = sm2.encryptHex(data, KeyType.PublicKey);
|
final String encryptHex = sm2.encryptHex(data, KeyType.PublicKey);
|
||||||
Console.log(encryptHex);
|
|
||||||
final String decryptStr = sm2.decryptStr(encryptHex, KeyType.PrivateKey);
|
final String decryptStr = sm2.decryptStr(encryptHex, KeyType.PrivateKey);
|
||||||
|
|
||||||
Assert.assertEquals(data, decryptStr);
|
Assert.assertEquals(data, decryptStr);
|
||||||
@ -236,11 +265,50 @@ public class SM2Test {
|
|||||||
|
|
||||||
String src = "Sm2Test";
|
String src = "Sm2Test";
|
||||||
byte[] data = sm2.encrypt(src, KeyType.PublicKey);
|
byte[] data = sm2.encrypt(src, KeyType.PublicKey);
|
||||||
byte[] sign = sm2.sign(src.getBytes());
|
byte[] sign = sm2.sign(src.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
Assert.assertTrue(sm2.verify( src.getBytes(), sign));
|
Assert.assertTrue(sm2.verify( src.getBytes(StandardCharsets.UTF_8), sign));
|
||||||
|
|
||||||
byte[] dec = sm2.decrypt(data, KeyType.PrivateKey);
|
byte[] dec = sm2.decrypt(data, KeyType.PrivateKey);
|
||||||
Assert.assertArrayEquals(dec, src.getBytes());
|
Assert.assertArrayEquals(dec, src.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getPublicKeyByPrivateKeyTest(){
|
||||||
|
// issue#I38SDP,openSSL生成的PKCS#1格式私钥
|
||||||
|
String priKey = "MHcCAQEEIE29XqAFV/rkJbnJzCoQRJLTeAHG2TR0h9ZCWag0+ZMEoAoGCCqBHM9VAYItoUQDQgAESkOzNigIsH5ehFvr9y" +
|
||||||
|
"QNQ66genyOrm+Q4umCA4aWXPeRzmcTAWSlTineiReTFN2lqor2xaulT8u3a4w3AM/F6A==";
|
||||||
|
|
||||||
|
PrivateKey privateKey = KeyUtil.generatePrivateKey("sm2", new OpenSSHPrivateKeySpec(SecureUtil.decode(priKey)));
|
||||||
|
final ECPrivateKeyParameters privateKeyParameters = ECKeyUtil.toPrivateParams(privateKey);
|
||||||
|
|
||||||
|
final SM2 sm2 = new SM2(privateKeyParameters, ECKeyUtil.getPublicParams(privateKeyParameters));
|
||||||
|
|
||||||
|
String src = "Sm2Test";
|
||||||
|
byte[] data = sm2.encrypt(src, KeyType.PublicKey);
|
||||||
|
byte[] sign = sm2.sign(src.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
Assert.assertTrue(sm2.verify( src.getBytes(StandardCharsets.UTF_8), sign));
|
||||||
|
|
||||||
|
byte[] dec = sm2.decrypt(data, KeyType.PrivateKey);
|
||||||
|
Assert.assertArrayEquals(dec, src.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readPublicKeyTest(){
|
||||||
|
String priKey = "MHcCAQEEIE29XqAFV/rkJbnJzCoQRJLTeAHG2TR0h9ZCWag0+ZMEoAoGCCqBHM9VAYItoUQDQgAESkOzNigIsH5ehFvr9y" +
|
||||||
|
"QNQ66genyOrm+Q4umCA4aWXPeRzmcTAWSlTineiReTFN2lqor2xaulT8u3a4w3AM/F6A==";
|
||||||
|
String pubKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAESkOzNigIsH5ehFvr9yQNQ66genyOrm+Q4umCA4aWXPeRzmcTAWSlTineiReTFN2lqor2xaulT8u3a4w3AM/F6A==";
|
||||||
|
|
||||||
|
SM2 sm2 = SmUtil.sm2(priKey, pubKey);
|
||||||
|
|
||||||
|
String src = "Sm2Test中文";
|
||||||
|
byte[] data = sm2.encrypt(src, KeyType.PublicKey);
|
||||||
|
byte[] sign = sm2.sign(src.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
Assert.assertTrue(sm2.verify( src.getBytes(StandardCharsets.UTF_8), sign));
|
||||||
|
|
||||||
|
byte[] dec = sm2.decrypt(data, KeyType.PrivateKey);
|
||||||
|
Assert.assertArrayEquals(dec, src.getBytes(StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package cn.hutool.crypto.test.symmetric;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class PBKDF2Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encryptTest(){
|
||||||
|
final String s = SecureUtil.pbkdf2("123456".toCharArray(), RandomUtil.randomBytes(16));
|
||||||
|
Assert.assertEquals(128, s.length());
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-db</artifactId>
|
<artifactId>hutool-db</artifactId>
|
||||||
|
@ -91,7 +91,6 @@ public class Entity extends Dict {
|
|||||||
|
|
||||||
// --------------------------------------------------------------- Constructor start
|
// --------------------------------------------------------------- Constructor start
|
||||||
public Entity() {
|
public Entity() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,7 +100,6 @@ public class Entity extends Dict {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public Entity(String tableName) {
|
public Entity(String tableName) {
|
||||||
super();
|
|
||||||
this.tableName = tableName;
|
this.tableName = tableName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import cn.hutool.core.convert.Convert;
|
|||||||
import cn.hutool.core.text.StrSpliter;
|
import cn.hutool.core.text.StrSpliter;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.core.util.CharUtil;
|
import cn.hutool.core.util.CharUtil;
|
||||||
|
import cn.hutool.core.util.NumberUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -444,7 +445,7 @@ public class Condition extends CloneSupport<Condition> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
valueStr = valueStr.trim();
|
valueStr = StrUtil.trim(valueStr);
|
||||||
|
|
||||||
// 处理null
|
// 处理null
|
||||||
if (StrUtil.endWithIgnoreCase(valueStr, "null")) {
|
if (StrUtil.endWithIgnoreCase(valueStr, "null")) {
|
||||||
@ -463,7 +464,7 @@ public class Condition extends CloneSupport<Condition> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> strs = StrUtil.split(valueStr, StrUtil.C_SPACE, 2);
|
final List<String> strs = StrUtil.split(valueStr, StrUtil.C_SPACE, 2);
|
||||||
if (strs.size() < 2) {
|
if (strs.size() < 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -472,7 +473,9 @@ public class Condition extends CloneSupport<Condition> {
|
|||||||
final String firstPart = strs.get(0).trim().toUpperCase();
|
final String firstPart = strs.get(0).trim().toUpperCase();
|
||||||
if (OPERATORS.contains(firstPart)) {
|
if (OPERATORS.contains(firstPart)) {
|
||||||
this.operator = firstPart;
|
this.operator = firstPart;
|
||||||
this.value = strs.get(1).trim();
|
// 比较符号后跟大部分为数字,此处做转换(IN不做转换)
|
||||||
|
final String valuePart = strs.get(1);
|
||||||
|
this.value = isOperatorIn() ? valuePart : tryToNumber(valuePart);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,5 +529,23 @@ public class Condition extends CloneSupport<Condition> {
|
|||||||
}
|
}
|
||||||
return value.substring(from, to);
|
return value.substring(from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试转换为数字,转换失败返回字符串
|
||||||
|
*
|
||||||
|
* @param value 被转换的字符串值
|
||||||
|
* @return 转换后的值
|
||||||
|
*/
|
||||||
|
private static Object tryToNumber(String value){
|
||||||
|
value = StrUtil.trim(value);
|
||||||
|
if(false == NumberUtil.isNumber(value)){
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
return NumberUtil.parseNumber(value);
|
||||||
|
} catch (Exception ignore){
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
// ----------------------------------------------------------------------------------------------- Private method end
|
// ----------------------------------------------------------------------------------------------- Private method end
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package cn.hutool.db;
|
|||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import cn.hutool.db.Entity;
|
|
||||||
import cn.hutool.db.pojo.User;
|
import cn.hutool.db.pojo.User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,10 +6,6 @@ import org.junit.Ignore;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import cn.hutool.core.lang.Console;
|
import cn.hutool.core.lang.Console;
|
||||||
import cn.hutool.db.Db;
|
|
||||||
import cn.hutool.db.Entity;
|
|
||||||
import cn.hutool.db.Page;
|
|
||||||
import cn.hutool.db.PageResult;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PostgreSQL 单元测试
|
* PostgreSQL 单元测试
|
||||||
|
@ -6,10 +6,6 @@ import org.junit.Ignore;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import cn.hutool.core.lang.Console;
|
import cn.hutool.core.lang.Console;
|
||||||
import cn.hutool.db.Db;
|
|
||||||
import cn.hutool.db.Entity;
|
|
||||||
import cn.hutool.db.Page;
|
|
||||||
import cn.hutool.db.PageResult;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SQL Server操作单元测试
|
* SQL Server操作单元测试
|
||||||
|
@ -52,4 +52,18 @@ public class ConditionTest {
|
|||||||
conditionBetween.setPlaceHolder(false);
|
conditionBetween.setPlaceHolder(false);
|
||||||
Assert.assertEquals("user BETWEEN 12 AND 13", conditionBetween.toString());
|
Assert.assertEquals("user BETWEEN 12 AND 13", conditionBetween.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseTest(){
|
||||||
|
final Condition age = Condition.parse("age", "< 10");
|
||||||
|
Assert.assertEquals("age < ?", age.toString());
|
||||||
|
// issue I38LTM
|
||||||
|
Assert.assertSame(Long.class, age.getValue().getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseInTest(){
|
||||||
|
final Condition age = Condition.parse("age", "in 1,2,3");
|
||||||
|
Assert.assertEquals("age IN (?,?,?)", age.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-dfa</artifactId>
|
<artifactId>hutool-dfa</artifactId>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-extra</artifactId>
|
<artifactId>hutool-extra</artifactId>
|
||||||
|
@ -110,6 +110,8 @@ public class Ftp extends AbstractFtp {
|
|||||||
* @param user 用户名
|
* @param user 用户名
|
||||||
* @param password 密码
|
* @param password 密码
|
||||||
* @param charset 编码
|
* @param charset 编码
|
||||||
|
* @param serverLanguageCode 服务器语言
|
||||||
|
* @param systemKey 系统关键字
|
||||||
* @param mode 模式
|
* @param mode 模式
|
||||||
*/
|
*/
|
||||||
public Ftp(String host, int port, String user, String password, Charset charset, String serverLanguageCode, String systemKey, FtpMode mode) {
|
public Ftp(String host, int port, String user, String password, Charset charset, String serverLanguageCode, String systemKey, FtpMode mode) {
|
||||||
@ -626,8 +628,8 @@ public class Ftp extends AbstractFtp {
|
|||||||
* @param fileName 文件名
|
* @param fileName 文件名
|
||||||
* @param out 输出位置
|
* @param out 输出位置
|
||||||
* @param fileNameCharset 文件名编码
|
* @param fileNameCharset 文件名编码
|
||||||
* @since 5.5.7
|
|
||||||
* @throws IORuntimeException IO异常
|
* @throws IORuntimeException IO异常
|
||||||
|
* @since 5.5.7
|
||||||
*/
|
*/
|
||||||
public void download(String path, String fileName, OutputStream out, Charset fileNameCharset) throws IORuntimeException {
|
public void download(String path, String fileName, OutputStream out, Charset fileNameCharset) throws IORuntimeException {
|
||||||
String pwd = null;
|
String pwd = null;
|
||||||
|
@ -21,7 +21,6 @@ public class UserPassAuthenticator extends Authenticator {
|
|||||||
* @param pass 密码
|
* @param pass 密码
|
||||||
*/
|
*/
|
||||||
public UserPassAuthenticator(String user, String pass) {
|
public UserPassAuthenticator(String user, String pass) {
|
||||||
super();
|
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.pass = pass;
|
this.pass = pass;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,6 @@ public class Connector {
|
|||||||
* @param password 密码
|
* @param password 密码
|
||||||
*/
|
*/
|
||||||
public Connector(String host, int port, String user, String password) {
|
public Connector(String host, int port, String user, String password) {
|
||||||
super();
|
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
@ -53,7 +53,6 @@ public class AviatorTest {
|
|||||||
Bar[] bars = new Bar[1];
|
Bar[] bars = new Bar[1];
|
||||||
|
|
||||||
public Foo(final int i, final float f, final Date date) {
|
public Foo(final int i, final float f, final Date date) {
|
||||||
super();
|
|
||||||
this.i = i;
|
this.i = i;
|
||||||
this.f = f;
|
this.f = f;
|
||||||
this.date = date;
|
this.date = date;
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-http</artifactId>
|
<artifactId>hutool-http</artifactId>
|
||||||
|
@ -378,6 +378,7 @@ public class HttpServerResponse extends HttpServerBase {
|
|||||||
* 返回文件给客户端(文件下载)
|
* 返回文件给客户端(文件下载)
|
||||||
*
|
*
|
||||||
* @param file 写出的文件对象
|
* @param file 写出的文件对象
|
||||||
|
* @param fileName 文件名
|
||||||
* @return this
|
* @return this
|
||||||
* @since 5.5.8
|
* @since 5.5.8
|
||||||
*/
|
*/
|
||||||
@ -417,6 +418,7 @@ public class HttpServerResponse extends HttpServerBase {
|
|||||||
* 返回文件数据给客户端(文件下载)
|
* 返回文件数据给客户端(文件下载)
|
||||||
*
|
*
|
||||||
* @param in 需要返回客户端的内容
|
* @param in 需要返回客户端的内容
|
||||||
|
* @param length 长度
|
||||||
* @param contentType 返回的类型
|
* @param contentType 返回的类型
|
||||||
* @param fileName 文件名
|
* @param fileName 文件名
|
||||||
* @since 5.2.7
|
* @since 5.2.7
|
||||||
|
@ -28,7 +28,6 @@ public class CustomProtocolsSSLFactory extends SSLSocketFactory {
|
|||||||
* @throws NoSuchAlgorithmException NoSuchAlgorithmException
|
* @throws NoSuchAlgorithmException NoSuchAlgorithmException
|
||||||
*/
|
*/
|
||||||
public CustomProtocolsSSLFactory(String... protocols) throws KeyManagementException, NoSuchAlgorithmException {
|
public CustomProtocolsSSLFactory(String... protocols) throws KeyManagementException, NoSuchAlgorithmException {
|
||||||
super();
|
|
||||||
this.protocols = protocols;
|
this.protocols = protocols;
|
||||||
this.base = SSLSocketFactoryBuilder.create().build();
|
this.base = SSLSocketFactoryBuilder.create().build();
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
public class DefaultSSLFactory extends CustomProtocolsSSLFactory {
|
public class DefaultSSLFactory extends CustomProtocolsSSLFactory {
|
||||||
|
|
||||||
public DefaultSSLFactory() throws KeyManagementException, NoSuchAlgorithmException {
|
public DefaultSSLFactory() throws KeyManagementException, NoSuchAlgorithmException {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -4,8 +4,6 @@ import cn.hutool.core.io.FileUtil;
|
|||||||
import cn.hutool.core.io.IORuntimeException;
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
import cn.hutool.core.io.StreamProgress;
|
import cn.hutool.core.io.StreamProgress;
|
||||||
import cn.hutool.core.lang.Console;
|
import cn.hutool.core.lang.Console;
|
||||||
import cn.hutool.http.HttpRequest;
|
|
||||||
import cn.hutool.http.HttpUtil;
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -3,8 +3,6 @@ package cn.hutool.http;
|
|||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import cn.hutool.http.HtmlUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Html单元测试
|
* Html单元测试
|
||||||
*
|
*
|
||||||
|
@ -318,11 +318,4 @@ public class HttpUtilTest {
|
|||||||
final String s = HttpUtil.get(url);
|
final String s = HttpUtil.get(url);
|
||||||
Console.log(s);
|
Console.log(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void tjhrTest(){
|
|
||||||
String url = "https://www.51tjhr.com";
|
|
||||||
final String s = HttpUtil.get(url);
|
|
||||||
Console.log(s);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package cn.hutool.http;
|
|||||||
|
|
||||||
import cn.hutool.core.lang.Console;
|
import cn.hutool.core.lang.Console;
|
||||||
import cn.hutool.core.thread.ThreadUtil;
|
import cn.hutool.core.thread.ThreadUtil;
|
||||||
import cn.hutool.http.HttpUtil;
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package cn.hutool.http;
|
package cn.hutool.http;
|
||||||
|
|
||||||
import cn.hutool.core.lang.Console;
|
import cn.hutool.core.lang.Console;
|
||||||
import cn.hutool.http.Header;
|
|
||||||
import cn.hutool.http.HttpRequest;
|
|
||||||
import cn.hutool.http.HttpUtil;
|
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-parent</artifactId>
|
<artifactId>hutool-parent</artifactId>
|
||||||
<version>5.5.9-SNAPSHOT</version>
|
<version>5.6.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hutool-json</artifactId>
|
<artifactId>hutool-json</artifactId>
|
||||||
|
@ -5,8 +5,8 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于定义<code>null</code>,与Javascript中null相对应<br>
|
* 用于定义{@code null},与Javascript中null相对应<br>
|
||||||
* Java中的<code>null</code>值在js中表示为undefined。
|
* Java中的{@code null}值在js中表示为undefined。
|
||||||
* @author Looly
|
* @author Looly
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -14,15 +14,15 @@ public class JSONNull implements Serializable{
|
|||||||
private static final long serialVersionUID = 2633815155870764938L;
|
private static final long serialVersionUID = 2633815155870764938L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>NULL</code> 对象用于减少歧义来表示Java 中的<code>null</code> <br>
|
* {@code NULL} 对象用于减少歧义来表示Java 中的{@code null} <br>
|
||||||
* <code>NULL.equals(null)</code> 返回 <code>true</code>. <br>
|
* {@code NULL.equals(null)} 返回 {@code true}. <br>
|
||||||
* <code>NULL.toString()</code> 返回 <code>"null"</code>.
|
* {@code NULL.toString()} 返回 {@code "null"}.
|
||||||
*/
|
*/
|
||||||
public static final JSONNull NULL = new JSONNull();
|
public static final JSONNull NULL = new JSONNull();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Null object is equal to the null value and to itself.
|
* A Null object is equal to the null value and to itself.
|
||||||
* 对象与其本身和<code>null</code>值相等
|
* 对象与其本身和{@code null}值相等
|
||||||
*
|
*
|
||||||
* @param object An object to test for nullness.
|
* @param object An object to test for nullness.
|
||||||
* @return true if the object parameter is the JSONObject.NULL object or null.
|
* @return true if the object parameter is the JSONObject.NULL object or null.
|
||||||
|
@ -35,7 +35,7 @@ import java.util.ResourceBundle;
|
|||||||
*
|
*
|
||||||
* @author Looly
|
* @author Looly
|
||||||
*/
|
*/
|
||||||
public final class JSONUtil {
|
public class JSONUtil {
|
||||||
|
|
||||||
// -------------------------------------------------------------------- Pause start
|
// -------------------------------------------------------------------- Pause start
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public class XML {
|
|||||||
/**
|
/**
|
||||||
* 转换XML为JSONObject
|
* 转换XML为JSONObject
|
||||||
* 转换过程中一些信息可能会丢失,JSON中无法区分节点和属性,相同的节点将被处理为JSONArray。
|
* 转换过程中一些信息可能会丢失,JSON中无法区分节点和属性,相同的节点将被处理为JSONArray。
|
||||||
* Content text may be placed in a "content" member. Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
* Content text may be placed in a "content" member. Comments, prologs, DTDs, and {@code <[ [ ]]>} are ignored.
|
||||||
*
|
*
|
||||||
* @param string The source string.
|
* @param string The source string.
|
||||||
* @return A JSONObject containing the structured data from the XML string.
|
* @return A JSONObject containing the structured data from the XML string.
|
||||||
@ -75,7 +75,7 @@ public class XML {
|
|||||||
/**
|
/**
|
||||||
* 转换XML为JSONObject
|
* 转换XML为JSONObject
|
||||||
* 转换过程中一些信息可能会丢失,JSON中无法区分节点和属性,相同的节点将被处理为JSONArray。
|
* 转换过程中一些信息可能会丢失,JSON中无法区分节点和属性,相同的节点将被处理为JSONArray。
|
||||||
* Content text may be placed in a "content" member. Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
* Content text may be placed in a "content" member. Comments, prologs, DTDs, and {@code <[ [ ]]>} are ignored.
|
||||||
* All values are converted as strings, for 1, 01, 29.0 will not be coerced to numbers but will instead be the exact value as seen in the XML document.
|
* All values are converted as strings, for 1, 01, 29.0 will not be coerced to numbers but will instead be the exact value as seen in the XML document.
|
||||||
*
|
*
|
||||||
* @param string The source string.
|
* @param string The source string.
|
||||||
|
@ -75,7 +75,6 @@ public class ResultDto<T> implements Serializable {
|
|||||||
* @param result the result
|
* @param result the result
|
||||||
*/
|
*/
|
||||||
public ResultDto(int code, String message, T result) {
|
public ResultDto(int code, String message, T result) {
|
||||||
super();
|
|
||||||
this.code(code).message(message).result(result);
|
this.code(code).message(message).result(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ public class CaseReport {
|
|||||||
/**
|
/**
|
||||||
* 包含的测试步骤报告
|
* 包含的测试步骤报告
|
||||||
*/
|
*/
|
||||||
private List<StepReport> stepReports = new ArrayList<StepReport>();
|
private List<StepReport> stepReports = new ArrayList<>();
|
||||||
|
|
||||||
public List<StepReport> getStepReports() {
|
public List<StepReport> getStepReports() {
|
||||||
return stepReports;
|
return stepReports;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user