This commit is contained in:
Looly
2023-03-26 08:53:56 +08:00
parent 1d4d4e0e76
commit f970dc7632
25 changed files with 308 additions and 98 deletions

View File

@@ -10,12 +10,7 @@ import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
/**
* Bean路径表达式用于获取多层嵌套Bean中的字段值或Bean对象<br>
@@ -119,11 +114,14 @@ public class BeanPath implements Serializable {
public void set(final Object bean, final Object value) {
Objects.requireNonNull(bean);
Object subBean = bean, previousBean;
Object subBean = bean;
Object previousBean = null;
boolean isFirst = true;
String patternPart;
// 尝试找到倒数第二个子对象, 最终需要设置它的字段值
final int length = patternParts.size() - 1;
// 填充父字段缺失的对象
for (int i = 0; i < length; i++) {
patternPart = patternParts.get(i);
// 保存当前操作的bean, 以便subBean不存在时, 可以用来填充缺失的子对象
@@ -145,8 +143,13 @@ public class BeanPath implements Serializable {
}
}
}
// 设置最终的字段值
BeanUtil.setFieldValue(subBean, patternParts.get(length), value);
// 设置最终的(当前)字段值
final Object newSubBean = BeanUtil.setFieldValue(subBean, patternParts.get(length), value);
if(newSubBean != subBean && null != previousBean){
// 对象变更,重新加入
BeanUtil.setFieldValue(previousBean, patternParts.get(length - 1), newSubBean);
}
}
@Override
@@ -166,7 +169,7 @@ public class BeanPath implements Serializable {
private Object get(final List<String> patternParts, final Object bean) {
Object subBean = bean;
boolean isFirst = true;
for (String patternPart : patternParts) {
for (final String patternPart : patternParts) {
subBean = getFieldValue(subBean, patternPart);
if (null == subBean) {
// 支持表达式的第一个对象为Bean本身若用户定义表达式$开头,则不做此操作)

View File

@@ -118,7 +118,7 @@ public class BeanUtil {
if (method.getParameterCount() == 0) {
final String name = method.getName();
if (name.startsWith("get") || name.startsWith("is")) {
if(false == "getClass".equals(name)){
if (false == "getClass".equals(name)) {
return true;
}
}
@@ -311,24 +311,32 @@ public class BeanUtil {
/**
* 设置字段值通过反射设置字段值并不调用setXXX方法<br>
* 对象同样支持Map类型fieldNameOrIndex即为key
* 对象同样支持Map类型fieldNameOrIndex即为key,支持:
* <ul>
* <li>Map</li>
* <li>List</li>
* <li>Bean</li>
* </ul>
*
* @param bean Bean
* @param fieldNameOrIndex 字段名或序号,序号支持负数
* @param value 值
* @return bean当为数组时返回一个新的数组
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static void setFieldValue(final Object bean, final String fieldNameOrIndex, final Object value) {
public static Object setFieldValue(final Object bean, final String fieldNameOrIndex, final Object value) {
if (bean instanceof Map) {
((Map) bean).put(fieldNameOrIndex, value);
} else if (bean instanceof List) {
ListUtil.setOrPadding((List) bean, Convert.toInt(fieldNameOrIndex), value);
} else if (ArrayUtil.isArray(bean)) {
ArrayUtil.setOrAppend(bean, Convert.toInt(fieldNameOrIndex), value);
// issue#3008追加产生新数组此处返回新数组
return ArrayUtil.setOrAppend(bean, Convert.toInt(fieldNameOrIndex), value);
} else {
// 普通Bean对象
FieldUtil.setFieldValue(bean, fieldNameOrIndex, value);
}
return bean;
}
/**

View File

@@ -701,7 +701,7 @@ public class IoUtil extends NioUtil {
* @return {@link InputStream}
* @since 4.0.9
*/
public static InputStream toMarkSupportStream(final InputStream in) {
public static InputStream toMarkSupport(final InputStream in) {
if (null == in) {
return null;
}
@@ -711,6 +711,23 @@ public class IoUtil extends NioUtil {
return in;
}
/**
* 将{@link Reader}转换为支持mark标记的Reader<br>
* 若原Reader支持mark标记则返回原Reader否则使用{@link BufferedReader} 包装之
*
* @param reader {@link Reader}
* @return {@link Reader}
*/
public static Reader toMarkSupport(final Reader reader) {
if (null == reader) {
return null;
}
if (false == reader.markSupported()) {
return new BufferedReader(reader);
}
return reader;
}
/**
* 获得{@link PushbackReader}<br>
* 如果是{@link PushbackReader}强转返回,否则新建

View File

@@ -53,6 +53,31 @@ public class ReaderWrapper extends Reader implements Wrapper<Reader> {
return raw.read(buffer, off, len);
}
@Override
public boolean markSupported() {
return this.raw.markSupported();
}
@Override
public void mark(final int readAheadLimit) throws IOException {
this.raw.mark(readAheadLimit);
}
@Override
public long skip(final long n) throws IOException {
return this.raw.skip(n);
}
@Override
public boolean ready() throws IOException {
return this.raw.ready();
}
@Override
public void reset() throws IOException {
this.raw.reset();
}
@Override
public void close() throws IOException {
raw.close();

View File

@@ -624,7 +624,8 @@ public class NetUtil {
}
/**
* 获取主机名称,一次获取会缓存名称
* 获取主机名称,一次获取会缓存名称<br>
* 注意此方法会触发反向DNS解析导致阻塞阻塞时间取决于网络
*
* @return 主机名称
* @since 5.4.4

View File

@@ -266,16 +266,17 @@ public class FieldUtil {
}
/**
* 设置字段值
* 设置字段值,如果值类型必须与字段类型匹配,会自动转换对象类型
*
* @param obj 对象如果是static字段此参数为null
* @param field 字段
* @param value 值,类型必须与字段类型匹配,不会自动转换对象类型
* @param value 值,类型不匹配会自动转换对象类型
* @throws UtilException UtilException 包装IllegalAccessException异常
*/
public static void setFieldValue(final Object obj, final Field field, Object value) throws UtilException {
Assert.notNull(field, "Field in [{}] not exist !", obj);
// 值类型检查和转换
final Class<?> fieldType = field.getType();
if (null != value) {
if (false == fieldType.isAssignableFrom(value.getClass())) {
@@ -290,6 +291,18 @@ public class FieldUtil {
value = ClassUtil.getDefaultValue(fieldType);
}
setFieldValueExact(obj, field, value);
}
/**
* 设置字段值,传入的字段值必须和字段类型一致,否则抛出异常
*
* @param obj 对象如果是static字段此参数为null
* @param field 字段
* @param value 值,值类型必须与字段类型匹配
* @throws UtilException UtilException 包装IllegalAccessException异常
*/
public static void setFieldValueExact(final Object obj, final Field field, final Object value) throws UtilException {
ReflectUtil.setAccessible(field);
try {
field.set(obj instanceof Class ? null : obj, value);

View File

@@ -3,6 +3,8 @@ package cn.hutool.core.bean;
import cn.hutool.core.lang.test.bean.ExamInfoDict;
import cn.hutool.core.lang.test.bean.UserInfoDict;
import cn.hutool.core.map.Dict;
import cn.hutool.core.util.ArrayUtil;
import lombok.Data;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -152,4 +154,36 @@ public class BeanPathTest {
BeanPath.of("aa.bb").set(dict, "BB");
Assert.assertEquals("{aa={bb=BB}}", dict.toString());
}
@Test
public void appendArrayTest(){
// issue#3008@Github
final MyUser myUser = new MyUser();
BeanPath.of("hobby[0]").set(myUser, "LOL");
BeanPath.of("hobby[1]").set(myUser, "KFC");
BeanPath.of("hobby[2]").set(myUser, "COFFE");
Assert.assertEquals("[LOL, KFC, COFFE]", ArrayUtil.toString(myUser.getHobby()));
}
@Test
public void appendArrayTest2(){
// issue#3008@Github
final MyUser2 myUser = new MyUser2();
BeanPath.of("myUser.hobby[0]").set(myUser, "LOL");
BeanPath.of("myUser.hobby[1]").set(myUser, "KFC");
BeanPath.of("myUser.hobby[2]").set(myUser, "COFFE");
Assert.assertEquals("[LOL, KFC, COFFE]", ArrayUtil.toString(myUser.getMyUser().getHobby()));
}
@Data
static class MyUser {
private String[] hobby;
}
@Data
static class MyUser2 {
private MyUser myUser;
}
}

View File

@@ -78,10 +78,17 @@ public class NetUtilTest {
}
@Test
@Ignore
public void getLocalHostNameTest() {
// 注意此方法会触发反向DNS解析导致阻塞阻塞时间取决于网络
Assert.assertNotNull(NetUtil.getLocalHostName());
}
@Test
public void getLocalHostTest() {
Assert.assertNotNull(NetUtil.getLocalhost());
}
@Test
public void pingTest(){
Assert.assertTrue(NetUtil.ping("127.0.0.1"));