Merged with v5-dev

This commit is contained in:
yeshwanthsripathy
2023-11-08 12:47:45 -06:00
95 changed files with 1115 additions and 300 deletions

View File

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

View File

@@ -251,7 +251,7 @@ public class Base64 {
* base64解码
*
* @param source 被解码的base64字符串
* @return 被加密后的字符串
* @return 密文解密的结果
* @since 4.3.2
*/
public static String decodeStrGbk(CharSequence source) {
@@ -262,7 +262,7 @@ public class Base64 {
* base64解码
*
* @param source 被解码的base64字符串
* @return 被加密后的字符串
* @return 密文解密的结果
*/
public static String decodeStr(CharSequence source) {
return Base64Decoder.decodeStr(source);
@@ -273,7 +273,7 @@ public class Base64 {
*
* @param source 被解码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
* @return 密文解密的结果
*/
public static String decodeStr(CharSequence source, String charset) {
return decodeStr(source, CharsetUtil.charset(charset));
@@ -284,7 +284,7 @@ public class Base64 {
*
* @param source 被解码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
* @return 密文解密的结果
*/
public static String decodeStr(CharSequence source, Charset charset) {
return Base64Decoder.decodeStr(source, charset);

View File

@@ -7,7 +7,6 @@ import cn.hutool.core.stream.CollectorUtil;
import cn.hutool.core.stream.StreamUtil;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

View File

@@ -2,7 +2,6 @@ package cn.hutool.core.collection;
import cn.hutool.core.comparator.PinyinComparator;
import cn.hutool.core.comparator.PropertyComparator;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Matcher;
import cn.hutool.core.lang.Validator;

View File

@@ -1,15 +1,12 @@
package cn.hutool.core.comparator;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.*;
import java.io.Serializable;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
/**
* 版本比较器<br>
@@ -24,6 +21,8 @@ import java.util.List;
public class VersionComparator implements Comparator<String>, Serializable {
private static final long serialVersionUID = 8083701245147495562L;
private static final Pattern PATTERN_PRE_NUMBERS= Pattern.compile("^\\d+");
/** 单例 */
public static final VersionComparator INSTANCE = new VersionComparator();
@@ -80,12 +79,15 @@ public class VersionComparator implements Comparator<String>, Serializable {
if (0 == diff) {
diff = v1.compareTo(v2);
}else {
//不同长度的先比较前面的数字;前面数字不相等时,按数字大小比较;数字相等的时候,继续按长度比较
int v1Num = Convert.toInt(ReUtil.get(PatternPool.NUMBERS, v1, 0), 0);
int v2Num = Convert.toInt(ReUtil.get(PatternPool.NUMBERS, v2, 0), 0);
int diff1 = v1Num - v2Num;
if (diff1 != 0) {
diff = diff1;
// 不同长度,且含有字母
if(!NumberUtil.isNumber(v1) || !NumberUtil.isNumber(v2)){
//不同长度的先比较前面的数字;前面数字不相等时,按数字大小比较;数字相等的时候,继续按长度比较,类似于 103 > 102a
final int v1Num = Convert.toInt(ReUtil.get(PATTERN_PRE_NUMBERS, v1, 0), 0);
final int v2Num = Convert.toInt(ReUtil.get(PATTERN_PRE_NUMBERS, v2, 0), 0);
final int diff1 = v1Num - v2Num;
if (diff1 != 0) {
diff = diff1;
}
}
}
if(diff != 0) {

View File

@@ -3,7 +3,6 @@ package cn.hutool.core.convert;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.impl.*;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.lang.TypeReference;

View File

@@ -6,7 +6,6 @@ import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.bean.copier.ValueProvider;
import cn.hutool.core.convert.AbstractConverter;
import cn.hutool.core.convert.ConvertException;
import cn.hutool.core.lang.Console;
import cn.hutool.core.map.MapProxy;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;

View File

@@ -133,7 +133,7 @@ public class Img implements Serializable {
* @return Img
*/
public static Img from(Image image) {
return new Img(ImgUtil.toBufferedImage(image));
return new Img(ImgUtil.castToBufferedImage(image, ImgUtil.IMAGE_TYPE_JPG));
}
/**

View File

@@ -455,11 +455,7 @@ public class ImgUtil {
* @param cols 目标切片列数。默认2必须是范围 [1, 20] 之内
*/
public static void sliceByRowsAndCols(File srcImageFile, File destDir, String format, int rows, int cols) {
try {
sliceByRowsAndCols(ImageIO.read(srcImageFile), destDir, format, rows, cols);
} catch (IOException e) {
throw new IORuntimeException(e);
}
sliceByRowsAndCols(read(srcImageFile), destDir, format, rows, cols);
}
/**
@@ -491,32 +487,27 @@ public class ImgUtil {
throw new IllegalArgumentException("Destination Dir must be a Directory !");
}
try {
if (rows <= 0 || rows > 20) {
rows = 2; // 切片行数
}
if (cols <= 0 || cols > 20) {
cols = 2; // 切片列数
}
// 读取源图像
final BufferedImage bi = toBufferedImage(srcImage);
int srcWidth = bi.getWidth(); // 源图宽度
int srcHeight = bi.getHeight(); // 源图高度
if (rows <= 0 || rows > 20) {
rows = 2; // 切片行数
}
if (cols <= 0 || cols > 20) {
cols = 2; // 切片列数
}
// 读取源图像
int srcWidth = srcImage.getWidth(null); // 源图宽度
int srcHeight = srcImage.getHeight(null); // 源图高度
int destWidth = NumberUtil.partValue(srcWidth, cols); // 每张切片的宽度
int destHeight = NumberUtil.partValue(srcHeight, rows); // 每张切片的高度
int destWidth = NumberUtil.partValue(srcWidth, cols); // 每张切片的宽度
int destHeight = NumberUtil.partValue(srcHeight, rows); // 每张切片的高度
// 循环建立切片
Image tag;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
tag = cut(bi, new Rectangle(j * destWidth, i * destHeight, destWidth, destHeight));
// 输出为文件
ImageIO.write(toRenderedImage(tag), format, new File(destDir, "_r" + i + "_c" + j + "." + format));
}
// 循环建立切片
Image tag;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
tag = cut(srcImage, new Rectangle(j * destWidth, i * destHeight, destWidth, destHeight));
// 输出为文件
write(tag, new File(destDir, "_r" + i + "_c" + j + "." + format));
}
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
@@ -573,8 +564,9 @@ public class ImgUtil {
* @since 4.1.14
*/
public static void convert(Image srcImage, String formatName, ImageOutputStream destImageStream, boolean isSrcPng) {
final BufferedImage src = toBufferedImage(srcImage, isSrcPng ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
try {
ImageIO.write(isSrcPng ? copyImage(srcImage, BufferedImage.TYPE_INT_RGB) : toBufferedImage(srcImage), formatName, destImageStream);
ImageIO.write(src, formatName, destImageStream);
} catch (IOException e) {
throw new IORuntimeException(e);
}
@@ -1157,13 +1149,11 @@ public class ImgUtil {
* @param img {@link Image}
* @return {@link BufferedImage}
* @since 4.3.2
* @deprecated 改用 {@link #castToRenderedImage(Image, String)}
*/
@Deprecated
public static RenderedImage toRenderedImage(Image img) {
if (img instanceof RenderedImage) {
return (RenderedImage) img;
}
return copyImage(img, BufferedImage.TYPE_INT_RGB);
return castToRenderedImage(img, IMAGE_TYPE_JPG);
}
/**
@@ -1172,13 +1162,44 @@ public class ImgUtil {
*
* @param img {@link Image}
* @return {@link BufferedImage}
* @deprecated 改用 {@link #castToBufferedImage(Image, String)}
*/
@Deprecated
public static BufferedImage toBufferedImage(Image img) {
return castToBufferedImage(img, IMAGE_TYPE_JPG);
}
/**
* {@link Image} 转 {@link RenderedImage}<br>
* 首先尝试强转,否则新建一个{@link BufferedImage}后重新绘制,使用 {@link BufferedImage#TYPE_INT_RGB} 模式。
*
* @param img {@link Image}
* @param imageType 目标图片类型例如jpg或png等
* @return {@link BufferedImage}
* @since 4.3.2
*/
public static RenderedImage castToRenderedImage(final Image img, final String imageType) {
if (img instanceof RenderedImage) {
return (RenderedImage) img;
}
return toBufferedImage(img, imageType);
}
/**
* {@link Image} 转 {@link BufferedImage}<br>
* 首先尝试强转,否则新建一个{@link BufferedImage}后重新绘制,使用 imageType 模式
*
* @param img {@link Image}
* @param imageType 目标图片类型例如jpg或png等
* @return {@link BufferedImage}
*/
public static BufferedImage castToBufferedImage(final Image img, final String imageType) {
if (img instanceof BufferedImage) {
return (BufferedImage) img;
}
return copyImage(img, BufferedImage.TYPE_INT_RGB);
return toBufferedImage(img, imageType);
}
/**
@@ -1687,7 +1708,7 @@ public class ImgUtil {
}
writer.setOutput(output);
final RenderedImage renderedImage = toRenderedImage(image);
final RenderedImage renderedImage = castToRenderedImage(image, IMAGE_TYPE_JPG);
// 设置质量
ImageWriteParam imgWriteParams = null;
if (quality > 0 && quality < 1) {

View File

@@ -606,7 +606,7 @@ public class GifDecoder {
for (int i = 0; i < 11; i++) {
app.append((char) block[i]);
}
if ("NETSCAPE2.0".equals(app.toString())) {
if ("NETSCAPE2.0".contentEquals(app)) {
readNetscapeExt();
} else {
skip(); // don't care

View File

@@ -1,10 +1,10 @@
package cn.hutool.core.io.file;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.visitor.CopyVisitor;
import cn.hutool.core.io.file.visitor.DelVisitor;
import cn.hutool.core.io.file.visitor.MoveVisitor;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.CharsetUtil;
@@ -20,7 +20,6 @@ import java.nio.charset.Charset;
import java.nio.file.AccessDeniedException;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryStream;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
@@ -33,6 +32,7 @@ import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
/**
* NIO中Path对象操作封装
@@ -80,11 +80,26 @@ public class PathUtil {
* @since 5.4.1
*/
public static List<File> loopFiles(Path path, int maxDepth, FileFilter fileFilter) {
return loopFiles(path, maxDepth, false, fileFilter);
}
/**
* 递归遍历目录以及子目录中的所有文件<br>
* 如果提供path为文件直接返回过滤结果
*
* @param path 当前遍历文件或目录
* @param maxDepth 遍历最大深度,-1表示遍历到没有目录为止
* @param isFollowLinks 是否跟踪软链(快捷方式)
* @param fileFilter 文件过滤规则对象选择要保留的文件只对文件有效不过滤目录null表示接收全部文件
* @return 文件列表
* @since 5.4.1
*/
public static List<File> loopFiles(final Path path, final int maxDepth, final boolean isFollowLinks, final FileFilter fileFilter) {
final List<File> fileList = new ArrayList<>();
if (null == path || false == Files.exists(path)) {
if (!exists(path, isFollowLinks)) {
return fileList;
} else if (false == isDirectory(path)) {
} else if (!isDirectory(path, isFollowLinks)) {
final File file = path.toFile();
if (null == fileFilter || fileFilter.accept(file)) {
fileList.add(file);
@@ -92,10 +107,10 @@ public class PathUtil {
return fileList;
}
walkFiles(path, maxDepth, new SimpleFileVisitor<Path>() {
walkFiles(path, maxDepth, isFollowLinks, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
public FileVisitResult visitFile(final Path path, final BasicFileAttributes attrs) {
final File file = path.toFile();
if (null == fileFilter || fileFilter.accept(file)) {
fileList.add(file);
@@ -129,14 +144,28 @@ public class PathUtil {
* @since 4.6.3
*/
public static void walkFiles(Path start, int maxDepth, FileVisitor<? super Path> visitor) {
walkFiles(start, maxDepth, false, visitor);
}
/**
* 遍历指定path下的文件并做处理
*
* @param start 起始路径,必须为目录
* @param maxDepth 最大遍历深度,-1表示不限制深度
* @param visitor {@link FileVisitor} 接口,用于自定义在访问文件时,访问目录前后等节点做的操作
* @param isFollowLinks 是否追踪到软链对应的真实地址
* @see Files#walkFileTree(Path, java.util.Set, int, FileVisitor)
* @since 5.8.23
*/
public static void walkFiles(final Path start, int maxDepth, final boolean isFollowLinks, final FileVisitor<? super Path> visitor) {
if (maxDepth < 0) {
// < 0 表示遍历到最底层
maxDepth = Integer.MAX_VALUE;
}
try {
Files.walkFileTree(start, EnumSet.noneOf(FileVisitOption.class), maxDepth, visitor);
} catch (IOException e) {
Files.walkFileTree(start, getFileVisitOption(isFollowLinks), maxDepth, visitor);
} catch (final IOException e) {
throw new IORuntimeException(e);
}
}
@@ -283,8 +312,7 @@ public class PathUtil {
if (null == path) {
return false;
}
final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
return Files.isDirectory(path, options);
return Files.isDirectory(path, getLinkOptions(isFollowLinks));
}
/**
@@ -368,9 +396,8 @@ public class PathUtil {
return null;
}
final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
try {
return Files.readAttributes(path, BasicFileAttributes.class, options);
return Files.readAttributes(path, BasicFileAttributes.class, getLinkOptions(isFollowLinks));
} catch (IOException e) {
throw new IORuntimeException(e);
}
@@ -541,8 +568,7 @@ public class PathUtil {
if (null == path) {
return false;
}
final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
return Files.isRegularFile(path, options);
return Files.isRegularFile(path, getLinkOptions(isFollowLinks));
}
/**
@@ -565,8 +591,7 @@ public class PathUtil {
* @since 5.5.3
*/
public static boolean exists(Path path, boolean isFollowLinks) {
final LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
return Files.exists(path, options);
return Files.exists(path, getLinkOptions(isFollowLinks));
}
/**
@@ -714,4 +739,27 @@ public class PathUtil {
}
}
}
/**
* 构建是否追踪软链的选项
*
* @param isFollowLinks 是否追踪软链
* @return 选项
* @since 5.8.23
*/
public static LinkOption[] getLinkOptions(final boolean isFollowLinks) {
return isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
}
/**
* 构建是否追踪软链的选项
*
* @param isFollowLinks 是否追踪软链
* @return 选项
* @since 5.8.23
*/
public static Set<FileVisitOption> getFileVisitOption(final boolean isFollowLinks) {
return isFollowLinks ? EnumSet.of(FileVisitOption.FOLLOW_LINKS) :
EnumSet.noneOf(FileVisitOption.class);
}
}

View File

@@ -1,14 +1,12 @@
package cn.hutool.core.io.resource;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.URLUtil;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
/**
* URL资源访问类

View File

@@ -185,7 +185,7 @@ public final class DataSize implements Comparable<DataSize> {
public static DataSize parse(CharSequence text, DataUnit defaultUnit) {
Assert.notNull(text, "Text must not be null");
try {
final Matcher matcher = PATTERN.matcher(text);
final Matcher matcher = PATTERN.matcher(StrUtil.cleanBlank(text));
Assert.state(matcher.matches(), "Does not match data size pattern");
final DataUnit unit = determineDataUnit(matcher.group(3), defaultUnit);

View File

@@ -5,6 +5,7 @@ import cn.hutool.core.collection.EnumerationIter;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.*;
@@ -356,22 +357,26 @@ public class ClassScanner implements Serializable {
}
/**
* 扫描jar包
* 扫描jar包扫描结束后关闭jar文件
*
* @param jar jar包
*/
private void scanJar(JarFile jar) {
String name;
for (JarEntry entry : new EnumerationIter<>(jar.entries())) {
name = StrUtil.removePrefix(entry.getName(), StrUtil.SLASH);
if (StrUtil.isEmpty(packagePath) || name.startsWith(this.packagePath)) {
if (name.endsWith(FileUtil.CLASS_EXT) && false == entry.isDirectory()) {
final String className = name//
try{
String name;
for (JarEntry entry : new EnumerationIter<>(jar.entries())) {
name = StrUtil.removePrefix(entry.getName(), StrUtil.SLASH);
if (StrUtil.isEmpty(packagePath) || name.startsWith(this.packagePath)) {
if (name.endsWith(FileUtil.CLASS_EXT) && false == entry.isDirectory()) {
final String className = name//
.substring(0, name.length() - 6)//
.replace(CharUtil.SLASH, CharUtil.DOT);//
addIfAccept(loadClass(className));
addIfAccept(loadClass(className));
}
}
}
} finally {
IoUtil.close(jar);
}
}

View File

@@ -198,6 +198,40 @@ public class Snowflake implements Serializable {
return (id >> TIMESTAMP_LEFT_SHIFT & ~(-1L << 41L)) + twepoch;
}
/**
* 根据传入时间戳-计算ID起终点
*
* @param timestampStart 开始时间戳
* @param timestampEnd 结束时间戳
* @return key-ID起点Value-ID终点
* @since 5.8.23
*/
public Pair<Long, Long> getIdScopeByTimestamp(long timestampStart, long timestampEnd) {
return getIdScopeByTimestamp(timestampStart, timestampEnd, true);
}
/**
* 根据传入时间戳-计算ID起终点 Gitee/issues/I60M14
*
* @param timestampStart 开始时间戳
* @param timestampEnd 结束时间戳
* @param ignoreCenterAndWorker 是否忽略数据中心和机器节点的占位,忽略后可获得分布式环境全局可信赖的起终点。
* @return key-ID起点Value-ID终点
* @since 5.8.23
*/
public Pair<Long, Long> getIdScopeByTimestamp(long timestampStart, long timestampEnd, boolean ignoreCenterAndWorker) {
long startTimeMinId = (timestampStart - twepoch) << TIMESTAMP_LEFT_SHIFT;
long endTimeMinId = (timestampEnd - twepoch) << TIMESTAMP_LEFT_SHIFT;
if (ignoreCenterAndWorker) {
long endId = endTimeMinId | ~(-1 << TIMESTAMP_LEFT_SHIFT);
return Pair.of(startTimeMinId, endId);
} else {
long startId = startTimeMinId | (dataCenterId << DATA_CENTER_ID_SHIFT) | (workerId << WORKER_ID_SHIFT);
long endId = endTimeMinId | (dataCenterId << DATA_CENTER_ID_SHIFT) | (workerId << WORKER_ID_SHIFT) | SEQUENCE_MASK;
return Pair.of(startId, endId);
}
}
/**
* 下一个ID
*

View File

@@ -30,7 +30,13 @@ public class BiMap<K, V> extends MapWrapper<K, V> {
@Override
public V put(K key, V value) {
final V oldValue = super.put(key, value);
if (null != this.inverse) {
if(null != oldValue){
// issue#I88R5M
// 如果put的key相同value不同需要在inverse中移除旧的关联
this.inverse.remove(oldValue);
}
this.inverse.put(value, key);
}
return super.put(key, value);

View File

@@ -5,7 +5,6 @@ import cn.hutool.core.map.MapWrapper;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**

View File

@@ -24,12 +24,18 @@ import java.util.*;
/**
* 网络相关工具
*
* @author xiaoleilu
* @author looly
*/
public class NetUtil {
/**
* 本地IPv4地址
*/
public final static String LOCAL_IP = Ipv4Util.LOCAL_IP;
/**
* 本地主机名称
*/
public static String localhostName;
/**
@@ -500,8 +506,8 @@ public class NetUtil {
final LinkedHashSet<InetAddress> localAddressList = localAddressList(address -> {
// 非loopback地址指127.*.*.*的地址
return false == address.isLoopbackAddress()
// 需为IPV4地址
&& address instanceof Inet4Address;
// 需为IPV4地址
&& address instanceof Inet4Address;
});
if (CollUtil.isNotEmpty(localAddressList)) {

View File

@@ -0,0 +1,85 @@
package cn.hutool.core.net;
import javax.net.SocketFactory;
import java.io.IOException;
import java.net.*;
/**
* 代理Socket工厂用于创建代理Socket<br>
* 来自commons-net的DefaultSocketFactory
*
* @author commons-net, looly
* @since 5.8.23
*/
public class ProxySocketFactory extends SocketFactory {
/**
* 创建代理SocketFactory
* @param proxy 代理对象
* @return {@code ProxySocketFactory}
*/
public static ProxySocketFactory of(final Proxy proxy) {
return new ProxySocketFactory(proxy);
}
private final Proxy proxy;
/**
* 构造
*
* @param proxy Socket代理
*/
public ProxySocketFactory(final Proxy proxy) {
this.proxy = proxy;
}
@Override
public Socket createSocket() {
if (proxy != null) {
return new Socket(proxy);
}
return new Socket();
}
@Override
public Socket createSocket(final InetAddress address, final int port) throws IOException {
if (proxy != null) {
final Socket s = new Socket(proxy);
s.connect(new InetSocketAddress(address, port));
return s;
}
return new Socket(address, port);
}
@Override
public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddr, final int localPort) throws IOException {
if (proxy != null) {
final Socket s = new Socket(proxy);
s.bind(new InetSocketAddress(localAddr, localPort));
s.connect(new InetSocketAddress(address, port));
return s;
}
return new Socket(address, port, localAddr, localPort);
}
@Override
public Socket createSocket(final String host, final int port) throws IOException {
if (proxy != null) {
final Socket s = new Socket(proxy);
s.connect(new InetSocketAddress(host, port));
return s;
}
return new Socket(host, port);
}
@Override
public Socket createSocket(final String host, final int port, final InetAddress localAddr, final int localPort) throws IOException {
if (proxy != null) {
final Socket s = new Socket(proxy);
s.bind(new InetSocketAddress(localAddr, localPort));
s.connect(new InetSocketAddress(host, port));
return s;
}
return new Socket(host, port, localAddr, localPort);
}
}

View File

@@ -1,6 +1,5 @@
package cn.hutool.core.net;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;

View File

@@ -187,7 +187,7 @@ public class ReflectUtil {
* 如果子类与父类中存在同名字段,则这两个字段同时存在,子类字段在前,父类字段在后。
*
* @param beanClass 类
* @param fieldFilter field过滤器过滤掉不需要的field
* @param fieldFilter field过滤器过滤掉不需要的field{@code null}返回原集合
* @return 字段列表
* @throws SecurityException 安全检查异常
* @since 5.7.14
@@ -286,14 +286,22 @@ public class ReflectUtil {
* @since 4.1.17
*/
public static Object[] getFieldsValue(Object obj) {
return getFieldsValue(obj, null);
}
/**
* 获取所有字段的值
*
* @param obj bean对象如果是static字段此处为类class
* @param filter 字段过滤器,,{@code null}返回原集合
* @return 字段值数组
* @since 5.8.23
*/
public static Object[] getFieldsValue(Object obj, Filter<Field> filter) {
if (null != obj) {
final Field[] fields = getFields(obj instanceof Class ? (Class<?>) obj : obj.getClass());
final Field[] fields = getFields(obj instanceof Class ? (Class<?>) obj : obj.getClass(), filter);
if (null != fields) {
final Object[] values = new Object[fields.length];
for (int i = 0; i < fields.length; i++) {
values[i] = getFieldValue(obj, fields[i]);
}
return values;
return ArrayUtil.map(fields, Object.class, field -> getFieldValue(obj, field));
}
}
return null;

View File

@@ -1,6 +1,5 @@
package cn.hutool.core.util;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.ParameterizedTypeImpl;
import cn.hutool.core.lang.reflect.ActualTypeMapperPool;
@@ -41,7 +40,10 @@ public class TypeUtil {
} else if (type instanceof ParameterizedType) {
return (Class<?>) ((ParameterizedType) type).getRawType();
} else if (type instanceof TypeVariable) {
return (Class<?>) ((TypeVariable<?>) type).getBounds()[0];
Type[] bounds = ((TypeVariable<?>) type).getBounds();
if (bounds.length == 1) {
return getClass(bounds[0]);
}
} else if (type instanceof WildcardType) {
final Type[] upperBounds = ((WildcardType) type).getUpperBounds();
if (upperBounds.length == 1) {

View File

@@ -29,20 +29,20 @@ public class AnnotationUtilTest {
final AnnotationForTest[] annotations = AnnotationUtil.getCombinationAnnotations(ClassWithAnnotation.class, AnnotationForTest.class);
Assert.assertNotNull(annotations);
Assert.assertEquals(1, annotations.length);
Assert.assertEquals("测试", annotations[0].value());
Assert.assertTrue(annotations[0].value().equals("测试") || annotations[0].value().equals("repeat-annotation"));
}
@Test
public void getAnnotationValueTest() {
final Object value = AnnotationUtil.getAnnotationValue(ClassWithAnnotation.class, AnnotationForTest.class);
Assert.assertEquals("测试", value);
Assert.assertTrue(value.equals("测试") || value.equals("repeat-annotation"));
}
@Test
public void getAnnotationValueTest2() {
final String[] names = AnnotationUtil.getAnnotationValue(ClassWithAnnotation.class, AnnotationForTest::names);
Assert.assertTrue(ArrayUtil.equals(names, new String[]{"测试1", "测试2"}));
Assert.assertTrue(names.length == 1 && names[0].isEmpty() || ArrayUtil.equals(names, new String[]{"测试1", "测试2"}));
}
@Test
@@ -52,7 +52,8 @@ public class AnnotationUtilTest {
// 加别名适配
final AnnotationForTest annotation = AnnotationUtil.getAnnotationAlias(ClassWithAnnotation.class, AnnotationForTest.class);
Assert.assertEquals("测试", annotation.retry());
String retryValue = annotation.retry();
Assert.assertTrue(retryValue.equals("测试") || retryValue.equals("repeat-annotation"));
Assert.assertTrue(AnnotationUtil.isSynthesizedAnnotation(annotation));
}
@@ -78,9 +79,12 @@ public class AnnotationUtilTest {
// -> RootMetaAnnotation3
final List<Annotation> annotations = AnnotationUtil.scanMetaAnnotation(RootAnnotation.class);
Assert.assertEquals(4, annotations.size());
Assert.assertEquals(RootMetaAnnotation3.class, annotations.get(0).annotationType());
Assert.assertEquals(RootMetaAnnotation1.class, annotations.get(1).annotationType());
Assert.assertEquals(RootMetaAnnotation2.class, annotations.get(2).annotationType());
Assert.assertTrue(annotations.get(0).annotationType() == RootMetaAnnotation3.class ||
annotations.get(0).annotationType() == RootMetaAnnotation1.class);
Assert.assertTrue(annotations.get(1).annotationType() == RootMetaAnnotation1.class ||
annotations.get(1).annotationType() == RootMetaAnnotation2.class);
Assert.assertTrue(annotations.get(2).annotationType() == RootMetaAnnotation2.class ||
annotations.get(2).annotationType() == RootMetaAnnotation3.class);
Assert.assertEquals(RootMetaAnnotation3.class, annotations.get(3).annotationType());
}

View File

@@ -15,36 +15,59 @@ public class VersionComparatorTest {
public void versionComparatorTest1() {
int compare = VersionComparator.INSTANCE.compare("1.2.1", "1.12.1");
Assert.assertTrue(compare < 0);
// 自反测试
compare = VersionComparator.INSTANCE.compare("1.12.1", "1.2.1");
Assert.assertTrue(compare > 0);
}
@Test
public void versionComparatorTest2() {
int compare = VersionComparator.INSTANCE.compare("1.12.1", "1.12.1c");
Assert.assertTrue(compare < 0);
compare = VersionComparator.INSTANCE.compare("1.12.1c", "1.12.1");
Assert.assertTrue(compare > 0);
}
@Test
public void versionComparatorTest3() {
int compare = VersionComparator.INSTANCE.compare(null, "1.12.1c");
Assert.assertTrue(compare < 0);
// 自反测试
compare = VersionComparator.INSTANCE.compare("1.12.1c", null);
Assert.assertTrue(compare > 0);
}
@Test
public void versionComparatorTest4() {
int compare = VersionComparator.INSTANCE.compare("1.13.0", "1.12.1c");
Assert.assertTrue(compare > 0);
// 自反测试
compare = VersionComparator.INSTANCE.compare("1.12.1c", "1.13.0");
Assert.assertTrue(compare < 0);
}
@Test
public void versionComparatorTest5() {
int compare = VersionComparator.INSTANCE.compare("V1.2", "V1.1");
Assert.assertTrue(compare > 0);
// 自反测试
compare = VersionComparator.INSTANCE.compare("V1.1", "V1.2");
Assert.assertTrue(compare < 0);
}
@Test
public void versionComparatorTes6() {
int compare = VersionComparator.INSTANCE.compare("V0.0.20170102", "V0.0.20170101");
Assert.assertTrue(compare > 0);
// 自反测试
compare = VersionComparator.INSTANCE.compare("V0.0.20170101", "V0.0.20170102");
Assert.assertTrue(compare < 0);
}
@Test
@@ -58,5 +81,9 @@ public class VersionComparatorTest {
public void versionComparatorTest7() {
int compare = VersionComparator.INSTANCE.compare("1.12.2", "1.12.1c");
Assert.assertTrue(compare > 0);
// 自反测试
compare = VersionComparator.INSTANCE.compare("1.12.1c", "1.12.2");
Assert.assertTrue(compare < 0);
}
}

View File

@@ -0,0 +1,16 @@
package cn.hutool.core.date;
import cn.hutool.core.lang.Console;
import org.junit.Assert;
import org.junit.Test;
public class Issue3348Test {
@Test
public void formatChineseDateTest() {
final String formatChineseDate = DateUtil.formatChineseDate(
DateUtil.parse("2023-10-23"), true, false);
Console.log(formatChineseDate);
Assert.assertEquals("二〇二三年十月二十三日", formatChineseDate);
}
}

View File

@@ -0,0 +1,12 @@
package cn.hutool.core.date;
import org.junit.Assert;
import org.junit.Test;
public class IssueI82Y1LTest {
@Test
public void parseTest() {
final String dt1 = "2023-09-14T05:00:03.648519Z";
Assert.assertEquals("2023-09-14 05:10:51", DateUtil.parse(dt1).toString());
}
}

View File

@@ -48,7 +48,9 @@ public class ImgUtilTest {
@Test
@Ignore
public void cutTest() {
ImgUtil.cut(FileUtil.file("d:/face.jpg"), FileUtil.file("d:/face_result.jpg"), new Rectangle(200, 200, 100, 100));
ImgUtil.cut(FileUtil.file("d:/test/hutool.png"),
FileUtil.file("d:/test/result.png"),
new Rectangle(0, 0, 400, 240));
}
@Test
@@ -92,6 +94,14 @@ public class ImgUtilTest {
ImgUtil.sliceByRowsAndCols(FileUtil.file("d:/temp/2.png"), FileUtil.file("d:/temp/slice/png"),ImgUtil.IMAGE_TYPE_PNG, 1, 5);
}
@Test
@Ignore
public void sliceByRowsAndColsTest2() {
ImgUtil.sliceByRowsAndCols(
FileUtil.file("d:/test/hutool.png"),
FileUtil.file("d:/test/dest"), ImgUtil.IMAGE_TYPE_PNG, 1, 5);
}
@Test
@Ignore
public void convertTest() {

View File

@@ -5,7 +5,6 @@ import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import java.io.File;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

View File

@@ -60,4 +60,11 @@ public class DataSizeUtilTest {
format = DataSizeUtil.format(1024L * 1024 * 1024 * 1024);
Assert.assertEquals("1 TB", format);
}
@Test
public void issueI88Z4ZTest() {
final String size = DataSizeUtil.format(10240000);
final long bytes = DataSize.parse(size).toBytes();
Assert.assertEquals(10244587, bytes);
}
}

View File

@@ -4,6 +4,7 @@ import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import org.junit.Assert;
import org.junit.Ignore;
@@ -19,22 +20,45 @@ import java.util.Set;
*/
public class SnowflakeTest {
/**
* 测试-根据传入时间戳-计算ID起终点
*/
@Test
public void snowflakeTestGetIdScope() {
final long workerId = RandomUtil.randomLong(31);
final long dataCenterId = RandomUtil.randomLong(31);
final Snowflake idWorker = new Snowflake(workerId, dataCenterId);
final long generatedId = idWorker.nextId();
// 随机忽略数据中心和工作机器的占位
final boolean ignore = RandomUtil.randomBoolean();
final long createTimestamp = idWorker.getGenerateDateTime(generatedId);
final Pair<Long, Long> idScope = idWorker.getIdScopeByTimestamp(createTimestamp, createTimestamp, ignore);
final long startId = idScope.getKey();
final long endId = idScope.getValue();
// 起点终点相差比较
final long trueOffSet = endId - startId;
// 忽略数据中心和工作机器时差值为22个1否则为12个1
final long expectedOffSet = ignore ? ~(-1 << 22) : ~(-1 << 12);
Assert.assertEquals(trueOffSet, expectedOffSet);
}
@Test
public void snowflakeTest1(){
//构建Snowflake提供终端ID和数据中心ID
Snowflake idWorker = new Snowflake(0, 0);
long nextId = idWorker.nextId();
final Snowflake idWorker = new Snowflake(0, 0);
final long nextId = idWorker.nextId();
Assert.assertTrue(nextId > 0);
}
@Test
public void snowflakeTest(){
HashSet<Long> hashSet = new HashSet<>();
final HashSet<Long> hashSet = new HashSet<>();
//构建Snowflake提供终端ID和数据中心ID
Snowflake idWorker = new Snowflake(0, 0);
final Snowflake idWorker = new Snowflake(0, 0);
for (int i = 0; i < 1000; i++) {
long id = idWorker.nextId();
final long id = idWorker.nextId();
hashSet.add(id);
}
Assert.assertEquals(1000L, hashSet.size());
@@ -43,8 +67,8 @@ public class SnowflakeTest {
@Test
public void snowflakeGetTest(){
//构建Snowflake提供终端ID和数据中心ID
Snowflake idWorker = new Snowflake(1, 2);
long nextId = idWorker.nextId();
final Snowflake idWorker = new Snowflake(1, 2);
final long nextId = idWorker.nextId();
Assert.assertEquals(1, idWorker.getWorkerId(nextId));
Assert.assertEquals(2, idWorker.getDataCenterId(nextId));
@@ -55,9 +79,9 @@ public class SnowflakeTest {
@Ignore
public void uniqueTest(){
// 测试并发环境下生成ID是否重复
Snowflake snowflake = IdUtil.getSnowflake(0, 0);
final Snowflake snowflake = IdUtil.getSnowflake(0, 0);
Set<Long> ids = new ConcurrentHashSet<>();
final Set<Long> ids = new ConcurrentHashSet<>();
ThreadUtil.concurrencyTest(100, () -> {
for (int i = 0; i < 50000; i++) {
if(false == ids.add(snowflake.nextId())){
@@ -94,7 +118,7 @@ public class SnowflakeTest {
final Snowflake snowflake = new Snowflake(null, 0, 0,
false, Snowflake.DEFAULT_TIME_OFFSET, 100);
Set<Long> ids = new ConcurrentHashSet<>();
final Set<Long> ids = new ConcurrentHashSet<>();
ThreadUtil.concurrencyTest(100, () -> {
for (int i = 0; i < 50000; i++) {
if(false == ids.add(snowflake.nextId())){

View File

@@ -0,0 +1,17 @@
package cn.hutool.core.map;
import org.junit.Assert;
import org.junit.Test;
import java.util.LinkedHashMap;
public class IssueI88R5MTest {
@Test
public void biMapTest() {
final BiMap<String, Integer> biMap = new BiMap<>(new LinkedHashMap<>());
biMap.put("aaa", 111);
biMap.getKey(111);
biMap.put("aaa", 222);
Assert.assertNull(biMap.getKey(111));
}
}

View File

@@ -1,7 +1,6 @@
package cn.hutool.core.map;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.StrUtil;

View File

@@ -1,6 +1,5 @@
package cn.hutool.core.text;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.CharUtil;
import org.junit.Assert;

View File

@@ -148,4 +148,9 @@ public class IdcardUtilTest {
flag = IdcardUtil.isValidTWCard(errTwCard2);
Assert.assertFalse(flag);
}
@Test
public void issueI88YKMTest() {
Assert.assertTrue(IdcardUtil.isValidCard("111111111111111"));
}
}

View File

@@ -1,48 +1,61 @@
package cn.hutool.core.util;
import lombok.Data;
import org.junit.Assert;
import org.junit.Test;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;
import org.junit.Assert;
import org.junit.Test;
public class TypeUtilTest {
@Test
public void getEleTypeTest() {
Method method = ReflectUtil.getMethod(TestClass.class, "getList");
Type type = TypeUtil.getReturnType(method);
Assert.assertEquals("java.util.List<java.lang.String>", type.toString());
Type type2 = TypeUtil.getTypeArgument(type);
Assert.assertEquals(String.class, type2);
}
@Test
public void getParamTypeTest() {
Method method = ReflectUtil.getMethod(TestClass.class, "intTest", Integer.class);
Type type = TypeUtil.getParamType(method, 0);
Assert.assertEquals(Integer.class, type);
Type returnType = TypeUtil.getReturnType(method);
Assert.assertEquals(Integer.class, returnType);
}
@Test
public void getClasses() {
Method method = ReflectUtil.getMethod(Parent.class, "getLevel");
Type returnType = TypeUtil.getReturnType(method);
Class clazz = TypeUtil.getClass(returnType);
Assert.assertEquals(Level1.class, clazz);
method = ReflectUtil.getMethod(Level1.class, "getId");
returnType = TypeUtil.getReturnType(method);
clazz = TypeUtil.getClass(returnType);
Assert.assertEquals(Object.class, clazz);
}
public static class TestClass {
public List<String> getList(){
public List<String> getList() {
return new ArrayList<>();
}
public Integer intTest(Integer integer) {
return 1;
}
}
@Test
public void getTypeArgumentTest(){
public void getTypeArgumentTest() {
// 测试不继承父类,而是实现泛型接口时是否可以获取成功。
final Type typeArgument = TypeUtil.getTypeArgument(IPService.class);
Assert.assertEquals(String.class, typeArgument);
@@ -59,25 +72,29 @@ public class TypeUtilTest {
}
@Test
public void getActualTypesTest(){
public void getActualTypesTest() {
// 测试多层级泛型参数是否能获取成功
Type idType = TypeUtil.getActualType(Level3.class,
ReflectUtil.getField(Level3.class, "id"));
Type idType = TypeUtil.getActualType(Level3.class, ReflectUtil.getField(Level3.class, "id"));
Assert.assertEquals(Long.class, idType);
}
public static class Level3 extends Level2<Level3>{
public static class Level3 extends Level2<Level3> {
}
public static class Level2<E> extends Level1<Long>{
public static class Level2<E> extends Level1<Long> {
}
@Data
public static class Level1<T>{
public static class Level1<T> {
private T id;
}
@Data
public static class Parent<T extends Level1<B>, B extends Long> {
private T level;
}
}