Merge branch 'v5-dev' of https://gitee.com/deligense/hutool into v5-dev

This commit is contained in:
hongzhe.qin
2021-11-01 10:58:31 +08:00
26 changed files with 448 additions and 213 deletions

View File

@@ -7,6 +7,7 @@ import cn.hutool.core.date.format.GlobalCustomFormat;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.SystemPropsUtil;
import java.sql.Timestamp;
import java.text.DateFormat;
@@ -288,7 +289,7 @@ public class DateTime extends Date {
* @see DatePattern
*/
public DateTime(CharSequence dateStr, DateParser dateParser) {
this(dateStr, dateParser, true);
this(dateStr, dateParser, SystemPropsUtil.getBoolean(SystemPropsUtil.HUTOOL_DATE_LENIENT, true));
}
/**

View File

@@ -262,6 +262,25 @@ public class Opt<T> {
return this;
}
/**
* 如果包裹里元素的值存在,就执行对应的操作集,并返回本身
* 如果不存在,返回一个空的{@code Opt}
*
* <p>属于 {@link #ifPresent}的链式拓展
* <p>属于 {@link #peek(Consumer)}的动态拓展
*
* @param actions 值存在时执行的操作,动态参数,可传入数组,当数组为一个空数组时并不会抛出 {@code NPE}
* @return this
* @throws NullPointerException 如果值存在,并且传入的操作集中的元素为 {@code null}
* @author VampireAchao
*/
@SafeVarargs
public final Opt<T> peeks(Consumer<T>... actions) throws NullPointerException {
// 第三个参数 (opts, opt) -> null其实并不会执行到该函数式接口所以直接返回了个null
return Stream.of(actions).reduce(this, Opt<T>::peek, (opts, opt) -> null);
}
/**
* 如果包裹里元素的值存在,就返回本身,如果不存在,则使用传入的操作执行后获得的 {@code Opt}
*

View File

@@ -16,17 +16,17 @@ import java.util.Objects;
public class Pair<K, V> extends CloneSupport<Pair<K, V>> implements Serializable {
private static final long serialVersionUID = 1L;
private final K key;
private final V value;
protected K key;
protected V value;
/**
* 构建{@link Pair}对象
* 构建{@code Pair}对象
*
* @param <K> 键类型
* @param <V> 值类型
* @param key 键
* @param value 值
* @return {@link Pair}
* @return {@code Pair}
* @since 5.4.3
*/
public static <K, V> Pair<K, V> of(K key, V value) {

View File

@@ -32,9 +32,11 @@ public interface RegexPool {
*/
String GROUP_VAR = "\\$(\\d+)";
/**
* IP v4
* IP v4<br>
* 采用分组方式便于解析地址的每一个段
*/
String IPV4 = "\\b((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\b";
//String IPV4 = "\\b((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\b";
String IPV4 = "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)$";
/**
* IP v6
*/

View File

@@ -3,7 +3,7 @@ package cn.hutool.core.lang.mutable;
import cn.hutool.core.util.NumberUtil;
/**
* 可变 <code>double</code> 类型
* 可变 {@code double} 类型
*
* @see Double
* @since 3.0.1
@@ -150,12 +150,12 @@ public class MutableDouble extends Number implements Comparable<MutableDouble>,
* 相等需同时满足如下条件:
* <ol>
* <li>非空</li>
* <li>类型为 {@link MutableDouble}</li>
* <li>类型为 {@code MutableDouble}</li>
* <li>值相等</li>
* </ol>
*
* @param obj 比对的对象
* @return 相同返回<code>true</code>,否则 <code>false</code>
* @return 相同返回<code>true</code>,否则 {@code false}
*/
@Override
public boolean equals(final Object obj) {
@@ -175,7 +175,7 @@ public class MutableDouble extends Number implements Comparable<MutableDouble>,
/**
* 比较
*
* @param other 其它 {@link MutableDouble} 对象
* @param other 其它 {@code MutableDouble} 对象
* @return x==y返回0x&lt;y返回-1x&gt;y返回1
*/
@Override

View File

@@ -0,0 +1,57 @@
package cn.hutool.core.lang.mutable;
import cn.hutool.core.lang.Pair;
/**
* 可变{@link Pair}实现,可以修改键和值
*
* @param <K> 键类型
* @param <V> 值类型
* @since 5.7.16
*/
public class MutablePair<K, V> extends Pair<K, V> implements Mutable<Pair<K, V>>{
private static final long serialVersionUID = 1L;
/**
* 构造
*
* @param key 键
* @param value 值
*/
public MutablePair(K key, V value) {
super(key, value);
}
/**
* 设置键
*
* @param key 新键
* @return this
*/
public MutablePair<K, V> setKey(K key) {
this.key = key;
return this;
}
/**
* 设置值
*
* @param value 新值
* @return this
*/
public MutablePair<K, V> setValue(V value) {
this.value = value;
return this;
}
@Override
public Pair<K, V> get() {
return this;
}
@Override
public void set(Pair<K, V> pair) {
this.key = pair.getKey();
this.value = pair.getValue();
}
}

View File

@@ -3,13 +3,14 @@ package cn.hutool.core.net;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.StrUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
/**
* IPV4地址工具类
@@ -20,6 +21,7 @@ import java.util.Objects;
* @since 5.4.1
*/
public class Ipv4Util {
/**
* IP段的分割符
*/
@@ -149,18 +151,25 @@ public class Ipv4Util {
/**
* 根据ip地址(xxx.xxx.xxx.xxx)计算出long型的数据
* 方法别名inet_aton
*
* @param strIP IP V4 地址
* @return long值
*/
public static long ipv4ToLong(String strIP) {
Validator.validateIpv4(strIP, "Invalid IPv4 address!");
final long[] ip = Convert.convert(long[].class, StrUtil.split(strIP, CharUtil.DOT));
return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];
final Matcher matcher = PatternPool.IPV4.matcher(strIP);
if (matcher.matches()) {
return matchAddress(matcher);
}
// Validator.validateIpv4(strIP, "Invalid IPv4 address!");
// final long[] ip = Convert.convert(long[].class, StrUtil.split(strIP, CharUtil.DOT));
// return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];
throw new IllegalArgumentException("Invalid IPv4 address!");
}
/**
* 根据 ip/掩码位 计算IP段的起始IP字符串型
* 方法别名inet_ntoa
*
* @param ip 给定的IP如218.240.38.69
* @param maskBit 给定的掩码位如30
* @return 起始IP的字符串表示
@@ -195,9 +204,7 @@ public class Ipv4Util {
* 根据子网掩码转换为掩码位
*
* @param mask 掩码的点分十进制表示,例如 255.255.255.0
*
* @return 掩码位,例如 24
*
* @throws IllegalArgumentException 子网掩码非法
*/
public static int getMaskBitByMask(String mask) {
@@ -282,7 +289,6 @@ public class Ipv4Util {
* 判断掩码是否合法
*
* @param mask 掩码的点分十进制表示,例如 255.255.255.0
*
* @return true掩码合法false掩码不合法
*/
public static boolean isMaskValid(String mask) {
@@ -293,7 +299,6 @@ public class Ipv4Util {
* 判断掩码位是否合法
*
* @param maskBit 掩码位,例如 24
*
* @return true掩码位合法false掩码位不合法
*/
public static boolean isMaskBitValid(int maskBit) {
@@ -315,5 +320,19 @@ public class Ipv4Util {
return getBeginIpLong(ip, maskBit)
+ ~ipv4ToLong(getMaskByMaskBit(maskBit));
}
/**
* 将匹配到的Ipv4地址的4个分组分别处理
*
* @param matcher 匹配到的Ipv4正则
* @return ipv4对应long
*/
private static long matchAddress(Matcher matcher) {
long addr = 0;
for (int i = 1; i <= 4; ++i) {
addr |= Long.parseLong(matcher.group(i)) << 8 * (4 - i);
}
return addr;
}
//-------------------------------------------------------------------------------- Private method end
}

View File

@@ -699,14 +699,15 @@ public class NetUtil {
* @since 4.0.6
*/
public static boolean isInRange(String ip, String cidr) {
String[] ips = StrUtil.splitToArray(ip, '.');
int ipAddr = (Integer.parseInt(ips[0]) << 24) | (Integer.parseInt(ips[1]) << 16) | (Integer.parseInt(ips[2]) << 8) | Integer.parseInt(ips[3]);
int type = Integer.parseInt(cidr.replaceAll(".*/", ""));
int mask = 0xFFFFFFFF << (32 - type);
String cidrIp = cidr.replaceAll("/.*", "");
String[] cidrIps = cidrIp.split("\\.");
int cidrIpAddr = (Integer.parseInt(cidrIps[0]) << 24) | (Integer.parseInt(cidrIps[1]) << 16) | (Integer.parseInt(cidrIps[2]) << 8) | Integer.parseInt(cidrIps[3]);
return (ipAddr & mask) == (cidrIpAddr & mask);
final int maskSplitMarkIndex = cidr.lastIndexOf(Ipv4Util.IP_MASK_SPLIT_MARK);
if(maskSplitMarkIndex < 0){
throw new IllegalArgumentException("Invalid cidr: " + cidr);
}
final long mask = (-1L << 32 - Integer.parseInt(cidr.substring(maskSplitMarkIndex + 1)));
long cidrIpAddr = ipv4ToLong(cidr.substring(0, maskSplitMarkIndex));
return (ipv4ToLong(ip) & mask) == (cidrIpAddr & mask);
}
/**

View File

@@ -22,7 +22,6 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
@@ -118,7 +117,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
public static <T> boolean hasNull(T... array) {
if (isNotEmpty(array)) {
for (T element : array) {
if (null == element) {
if (ObjectUtil.isNull(element)) {
return true;
}
}
@@ -150,7 +149,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
*/
@SuppressWarnings("unchecked")
public static <T> T firstNonNull(T... array) {
return firstMatch(Objects::nonNull, array);
return firstMatch(ObjectUtil::isNotNull, array);
}
/**

View File

@@ -292,7 +292,7 @@ public class ObjectUtil {
* @since 3.0.7
*/
public static <T> T defaultIfNull(final T object, final T defaultValue) {
return (null != object) ? object : defaultValue;
return isNull(object) ? defaultValue : object;
}
@@ -300,14 +300,14 @@ public class ObjectUtil {
* 如果给定对象为{@code null} 返回默认值, 如果不为null 返回自定义handle处理后的返回值
*
* @param source Object 类型对象
* @param handle 自定义的处理方法
* @param handle 非空时自定义的处理方法
* @param defaultValue 默认为空的返回值
* @param <T> 被检查对象为{@code null}返回默认值否则返回自定义handle处理后的返回值
* @return 处理后的返回值
* @since 5.4.6
*/
public static <T> T defaultIfNull(Object source, Supplier<? extends T> handle, final T defaultValue) {
if (Objects.nonNull(source)) {
if (isNotNull(source)) {
return handle.get();
}
return defaultValue;

View File

@@ -0,0 +1,146 @@
package cn.hutool.core.util;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Console;
import java.util.Properties;
/**
* 系统属性工具<br>
* 此工具用于读取系统属性或环境变量信息,封装包括:
* <ul>
* <li>{@link System#getProperty(String)}</li>
* <li>{@link System#getenv(String)}</li>
* </ul>
*
* @author looly
* @since 5.7.16
*/
public class SystemPropsUtil {
/** Hutool自定义系统属性是否解析日期字符串采用严格模式 */
public static String HUTOOL_DATE_LENIENT = "hutool.date.lenient";
/**
* 取得系统属性如果因为Java安全的限制而失败则将错误打在Log中然后返回 defaultValue
*
* @param name 属性名
* @param defaultValue 默认值
* @return 属性值或defaultValue
* @see System#getProperty(String)
* @see System#getenv(String)
*/
public static String get(String name, String defaultValue) {
return StrUtil.nullToDefault(get(name, false), defaultValue);
}
/**
* 取得系统属性如果因为Java安全的限制而失败则将错误打在Log中然后返回 {@code null}
*
* @param name 属性名
* @param quiet 安静模式,不将出错信息打在{@code System.err}中
* @return 属性值或{@code null}
* @see System#getProperty(String)
* @see System#getenv(String)
*/
public static String get(String name, boolean quiet) {
String value = null;
try {
value = System.getProperty(name);
} catch (SecurityException e) {
if (false == quiet) {
Console.error("Caught a SecurityException reading the system property '{}'; " +
"the SystemUtil property value will default to null.", name);
}
}
if (null == value) {
try {
value = System.getenv(name);
} catch (SecurityException e) {
if (false == quiet) {
Console.error("Caught a SecurityException reading the system env '{}'; " +
"the SystemUtil env value will default to null.", name);
}
}
}
return value;
}
/**
* 获得System属性
*
* @param key 键
* @return 属性值
* @see System#getProperty(String)
* @see System#getenv(String)
*/
public static String get(String key) {
return get(key, null);
}
/**
* 获得boolean类型值
*
* @param key 键
* @param defaultValue 默认值
* @return 值
*/
public static boolean getBoolean(String key, boolean defaultValue) {
String value = get(key);
if (value == null) {
return defaultValue;
}
value = value.trim().toLowerCase();
if (value.isEmpty()) {
return true;
}
return Convert.toBool(value, defaultValue);
}
/**
* 获得int类型值
*
* @param key 键
* @param defaultValue 默认值
* @return 值
*/
public static long getInt(String key, int defaultValue) {
return Convert.toInt(get(key), defaultValue);
}
/**
* 获得long类型值
*
* @param key 键
* @param defaultValue 默认值
* @return 值
*/
public static long getLong(String key, long defaultValue) {
return Convert.toLong(get(key), defaultValue);
}
/**
* @return 属性列表
*/
public static Properties getProps() {
return System.getProperties();
}
/**
* 设置系统属性value为{@code null}表示移除此属性
*
* @param key 属性名
* @param value 属性值,{@code null}表示移除此属性
*/
public static void set(String key, String value) {
if (null == value) {
System.clearProperty(key);
} else {
System.setProperty(key, value);
}
}
}

View File

@@ -63,6 +63,34 @@ public class OptTest {
Assert.assertEquals("hutool", name);
}
@Test
public void peeksTest() {
User user = new User();
// 相当于上面peek的动态参数调用更加灵活你可以像操作数组一样去动态设置中间的步骤也可以使用这种方式去编写你的代码
// 可以一行搞定
Opt.ofNullable("hutool").peeks(user::setUsername, user::setNickname);
// 也可以在适当的地方换行使得代码的可读性提高
Opt.of(user).peeks(
u -> Assert.assertEquals("hutool", u.getNickname()),
u -> Assert.assertEquals("hutool", u.getUsername())
);
Assert.assertEquals("hutool", user.getNickname());
Assert.assertEquals("hutool", user.getUsername());
// 注意传入的lambda中对包裹内的元素执行赋值操作并不会影响到原来的元素,这是java语言的特性。。。
// 这也是为什么我们需要getter和setter而不直接给bean中的属性赋值中的其中一个原因
String name = Opt.ofNullable("hutool").peeks(
username -> username = "123", username -> username = "456",
n -> Assert.assertEquals("hutool", n)).get();
Assert.assertEquals("hutool", name);
// 当然以下情况不会抛出NPE但也没什么意义
Opt.ofNullable("hutool").peeks().peeks().peeks();
Opt.ofNullable(null).peeks(i -> {
});
}
@Test
public void orTest() {
// 这是jdk9 Optional中的新函数直接照搬了过来

View File

@@ -21,7 +21,7 @@ public class ValidatorTest {
}
@Test
public void hasNumberTest() throws Exception {
public void hasNumberTest() {
String var1 = "";
String var2 = "str";
String var3 = "180";
@@ -218,4 +218,13 @@ public class ValidatorTest {
public void isCarDrivingLicenceTest(){
Assert.assertTrue(Validator.isCarDrivingLicence("430101758218"));
}
@Test
public void validateIpv4Test(){
Validator.validateIpv4("192.168.1.1", "Error ip");
Validator.validateIpv4("8.8.8.8", "Error ip");
Validator.validateIpv4("0.0.0.0", "Error ip");
Validator.validateIpv4("255.255.255.255", "Error ip");
Validator.validateIpv4("127.0.0.0", "Error ip");
}
}

View File

@@ -1,11 +1,10 @@
package cn.hutool.core.net;
import cn.hutool.core.lang.Console;
import org.junit.Assert;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import java.util.List;
import org.junit.function.ThrowingRunnable;
public class Ipv4UtilTest {
@@ -40,7 +39,7 @@ public class Ipv4UtilTest {
String ip = "192.168.1.1";
final int maskBitByMask = Ipv4Util.getMaskBitByMask("255.255.255.0");
final String endIpStr = Ipv4Util.getEndIpStr(ip, maskBitByMask);
Console.log(endIpStr);
Assert.assertEquals("192.168.1.255", endIpStr);
}
@Test
@@ -75,4 +74,16 @@ public class Ipv4UtilTest {
boolean maskBitValid = Ipv4Util.isMaskBitValid(33);
Assert.assertFalse("掩码位非法检验", maskBitValid);
}
@Test
public void ipv4ToLongTest(){
long l = Ipv4Util.ipv4ToLong("127.0.0.1");
Assert.assertEquals(2130706433L, l);
l = Ipv4Util.ipv4ToLong("114.114.114.114");
Assert.assertEquals(1920103026L, l);
l = Ipv4Util.ipv4ToLong("0.0.0.0");
Assert.assertEquals(0L, l);
l = Ipv4Util.ipv4ToLong("255.255.255.255");
Assert.assertEquals(4294967295L, l);
}
}

View File

@@ -101,4 +101,15 @@ public class NetUtilTest {
Console.log(txt);
}
@Test
public void isInRangeTest(){
Assert.assertTrue(NetUtil.isInRange("114.114.114.114","0.0.0.0/0"));
Assert.assertTrue(NetUtil.isInRange("192.168.3.4","192.0.0.0/8"));
Assert.assertTrue(NetUtil.isInRange("192.168.3.4","192.168.0.0/16"));
Assert.assertTrue(NetUtil.isInRange("192.168.3.4","192.168.3.0/24"));
Assert.assertTrue(NetUtil.isInRange("192.168.3.4","192.168.3.4/32"));
Assert.assertFalse(NetUtil.isInRange("8.8.8.8","192.0.0.0/8"));
Assert.assertFalse(NetUtil.isInRange("114.114.114.114","192.168.3.4/32"));
}
}