This commit is contained in:
Looly
2024-09-23 15:08:20 +08:00
parent ff187f6fde
commit 2872f15e91
49 changed files with 796 additions and 600 deletions

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.core.annotation;
import java.lang.annotation.*;
/**
* 标记可读Bean<br>
* 即包含有可读字段的Bean使用此注解标记如含有public的字段或getter方法
*
* @author looly
* @since 6.0.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface ReadableBean {
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.core.annotation;
import java.lang.annotation.*;
/**
* 标记可写Bean<br>
* 即包含有可写字段的Bean使用此注解标记如含有public的字段或setter方法
*
* @author looly
* @since 6.0.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface WritableBean {
}

View File

@@ -16,6 +16,9 @@
package org.dromara.hutool.core.bean;
import org.dromara.hutool.core.annotation.AnnotationUtil;
import org.dromara.hutool.core.annotation.ReadableBean;
import org.dromara.hutool.core.annotation.WritableBean;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.bean.copier.BeanCopier;
import org.dromara.hutool.core.bean.copier.CopyOptions;
@@ -687,6 +690,11 @@ public class BeanUtil {
// String中有getter方法但为字符串不是Bean
return false;
}
if(AnnotationUtil.hasAnnotation(clazz, ReadableBean.class)){
return true;
}
return hasGetter(clazz) || hasPublicField(clazz);
}
@@ -712,6 +720,10 @@ public class BeanUtil {
return false;
}
if(AnnotationUtil.hasAnnotation(clazz, WritableBean.class)){
return true;
}
return hasSetter(clazz) || hasPublicField(clazz);
}

View File

@@ -31,7 +31,7 @@ public interface ValueProvider<K>{
/**
* 获取值<br>
* 返回值一般需要匹配被注入类型,如果不匹配会调用默认转换 Convert#convert(Type, Object)实现转换
* 返回值一般需要匹配被注入类型,如果不匹配会调用默认转换 Convert#support(Type, Object)实现转换
*
* @param key Bean对象中参数名
* @param valueType 被注入的值的类型

View File

@@ -17,14 +17,11 @@
package org.dromara.hutool.core.bean.path;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.bean.path.node.NameNode;
import org.dromara.hutool.core.bean.path.node.Node;
import org.dromara.hutool.core.bean.path.node.NodeFactory;
import org.dromara.hutool.core.text.CharUtil;
import org.dromara.hutool.core.text.StrUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
/**
@@ -67,6 +64,7 @@ public class BeanPath implements Iterator<BeanPath> {
private final Node node;
private final String child;
private NodeBeanCreator beanCreator;
/**
* 构造
@@ -74,6 +72,7 @@ public class BeanPath implements Iterator<BeanPath> {
* @param expression 表达式
*/
public BeanPath(final String expression) {
this.beanCreator = DefaultNodeBeanCreator.INSTANCE;
final int length = expression.length();
final StringBuilder builder = new StringBuilder();
@@ -128,6 +127,17 @@ public class BeanPath implements Iterator<BeanPath> {
}
}
/**
* 设置Bean创建器用于创建Bean对象默认为{@link DefaultNodeBeanCreator}
*
* @param beanCreator Bean创建器
* @return this
*/
public BeanPath setBeanCreator(final NodeBeanCreator beanCreator) {
this.beanCreator = beanCreator;
return this;
}
/**
* 获取节点
*
@@ -189,7 +199,7 @@ public class BeanPath implements Iterator<BeanPath> {
final BeanPath childBeanPath = next();
Object subBean = this.node.getValue(bean);
if (null == subBean) {
subBean = isListNode(childBeanPath.node) ? new ArrayList<>() : new HashMap<>();
subBean = beanCreator.create(bean, this);
this.node.setValue(bean, subBean);
// 如果自定义put方法修改了value返回修改后的value避免值丢失
subBean = this.node.getValue(bean);
@@ -210,17 +220,4 @@ public class BeanPath implements Iterator<BeanPath> {
", child='" + child + '\'' +
'}';
}
/**
* 子节点值是否为列表
*
* @param node 节点
* @return 是否为列表
*/
private static boolean isListNode(final Node node) {
if (node instanceof NameNode) {
return ((NameNode) node).isNumber();
}
return false;
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.core.bean.path;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.bean.path.node.NameNode;
import org.dromara.hutool.core.bean.path.node.Node;
import org.dromara.hutool.core.reflect.ConstructorUtil;
import org.dromara.hutool.core.reflect.FieldUtil;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 默认的Bean创建器
*
* @author looly
* @since 6.0.0
*/
public class DefaultNodeBeanCreator implements NodeBeanCreator {
/**
* 单例
*/
public static final NodeBeanCreator INSTANCE = new DefaultNodeBeanCreator();
@Override
public Object create(final Object parent, final BeanPath beanPath) {
if(parent instanceof Map || parent instanceof List || ArrayUtil.isArray(parent)){
// 根据下一个节点类型,判断当前节点名称对应类型
final Node node = beanPath.next().getNode();
if (node instanceof NameNode) {
return ((NameNode) node).isNumber() ? new ArrayList<>() : new HashMap<>();
}
return new HashMap<>();
}
// 普通Bean
final Node node = beanPath.getNode();
if(node instanceof NameNode){
final String name = ((NameNode) node).getName();
final Field field = FieldUtil.getField(parent.getClass(), name);
if(null == field){
throw new IllegalArgumentException("No field found for name: " + name);
}
return ConstructorUtil.newInstanceIfPossible(field.getType());
}
throw new UnsupportedOperationException("Unsupported node type: " + node.getClass());
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.core.bean.path;
/**
* BeanPath节点对应的Bean创建器<br>
* 用于创建Bean路径节点对应的Bean
*
* @author looly
* @since 6.0.0
*/
public interface NodeBeanCreator {
/**
* 创建Bean<br>
* beanPath对应当前的路径即如果父对象为a则beanPath为a.b则创建的Bean为a.b.c对应的Bean对象<br>
* 给定的a一定存在但是本路径中b对应的Bean不存在则创建的对象是b的值这个值用c表示
*
* @param parent 父Bean
* @param beanPath 当前路径
* @return Bean
*/
Object create(final Object parent, final BeanPath beanPath);
}

View File

@@ -34,6 +34,15 @@ public class NameNode implements Node {
private final String name;
/**
* 获取节点名
*
* @return 节点名
*/
public String getName() {
return name;
}
/**
* 构造
*

View File

@@ -505,7 +505,7 @@ public class Hashids implements Encoder<long[], String>, Decoder<String, long[]>
return IntStream.range(0, separators.length)
.mapToObj(idx -> (separators[idx]))
.filter(valid::contains)
// ugly way to convert back to char[]
// ugly way to support back to char[]
.map(c -> Character.toString(c))
.collect(Collectors.joining())
.toCharArray();

View File

@@ -135,7 +135,7 @@ public class PercentCodec implements Encoder<byte[], byte[]>, Serializable {
// 对于空格单独处理
rewrittenPath.append('+');
} else {
// convert to external encoding before hex conversion
// support to external encoding before hex conversion
try {
writer.write(c);
writer.flush();

View File

@@ -67,7 +67,7 @@ public class Z85Codec implements Encoder<byte[], String>, Decoder<String, byte[]
// Support inputs that are not divisible by 4 with no remainder.
final int padding = 4 - (remainder == 0 ? 4 : remainder);
// SPEC: 4 octets convert into 5 printable characters.
// SPEC: 4 octets support into 5 printable characters.
// char.length = length + 1/4 length == 5/4 length.
final char[] chars = new char[length + (length >>> 2) + (remainder == 0 ? 0 : 1)];
int idx = 0;

View File

@@ -45,7 +45,7 @@ public abstract class AbstractConverter implements Converter, Serializable {
return null;
}
if (TypeUtil.isUnknown(targetType)) {
throw new ConvertException("Unsupported convert to unKnown type: {}", targetType);
throw new ConvertException("Unsupported support to unKnown type: {}", targetType);
}
final Class<?> targetClass = TypeUtil.getClass(targetType);

View File

@@ -201,6 +201,6 @@ public class CompositeConverter implements Converter, Serializable {
}
// 无法转换
throw new ConvertException("Can not convert from {}: [{}] to [{}]", value.getClass().getName(), value, type.getTypeName());
throw new ConvertException("Can not support from {}: [{}] to [{}]", value.getClass().getName(), value, type.getTypeName());
}
}

View File

@@ -90,7 +90,7 @@ public class RegisterConverter extends ConverterWithRoot implements Serializable
}
// 无法转换
throw new ConvertException("Can not convert from {}: [{}] to [{}]", value.getClass().getName(), value, targetType.getTypeName());
throw new ConvertException("Can not support from {}: [{}] to [{}]", value.getClass().getName(), value, targetType.getTypeName());
}
/**

View File

@@ -108,7 +108,7 @@ public class DateConverter extends AbstractConverter implements MatcherConverter
}
}
throw new ConvertException("Can not convert {}:[{}] to {}", value.getClass().getName(), value, targetClass.getName());
throw new ConvertException("Can not support {}:[{}] to {}", value.getClass().getName(), value, targetClass.getName());
}
/**

View File

@@ -67,7 +67,7 @@ public class EnumConverter extends AbstractConverter implements MatcherConverter
return enumValue;
}
throw new ConvertException("Can not convert {} to {}", value, targetClass);
throw new ConvertException("Can not support {} to {}", value, targetClass);
}
/**

View File

@@ -78,7 +78,7 @@ public class NumberConverter extends AbstractConverter implements MatcherConvert
protected String convertToStr(final Object value) {
final String result = StrUtil.trim(super.convertToStr(value));
if(StrUtil.isEmpty(result)){
throw new ConvertException("Can not convert empty value to Number!");
throw new ConvertException("Can not support empty value to Number!");
}
if (result.length() > 1) {

View File

@@ -76,7 +76,7 @@ public class PrimitiveConverter extends AbstractConverter implements MatcherConv
}
if(null == result){
throw new ConvertException("Can not convert {} to {}", value, primitiveClass);
throw new ConvertException("Can not support {} to {}", value, primitiveClass);
}
return result;

View File

@@ -408,7 +408,7 @@ public class FastDateFormat extends Format implements PositionDateParser, DatePr
return printer.getMaxLengthEstimate();
}
// convert DateTimeFormatter
// support DateTimeFormatter
// -----------------------------------------------------------------------
/**

View File

@@ -266,7 +266,7 @@ public class Version implements Comparable<Version>, Serializable {
}
return c;
}
// Types differ, so convert number to string form
// Types differ, so support number to string form
final int c = o1.toString().compareTo(o2.toString());
if (c == 0){
continue;

View File

@@ -329,7 +329,7 @@ public class ConvertTest {
@Test
public void toClassTest(){
final Class<?> convert = ConvertUtil.convert(Class.class, "org.dromara.hutool.core.convert.ConvertTest.Product");
final Class<?> convert = ConvertUtil.convert(Class.class, "org.dromara.hutool.core.support.ConvertTest.Product");
Assertions.assertSame(Product.class, convert);
}

View File

@@ -219,7 +219,7 @@ public class ByteUtilTest {
final byte[] bytes = new byte[]{(byte) 0xFF, (byte) 0xFE, (byte) 0x01, (byte) 0x00};
final int expectedValue = 130815;
final int actualValue = ByteUtil.toInt(bytes, 0, ByteOrder.LITTLE_ENDIAN);
assertEquals(expectedValue, actualValue, "Failed to convert bytes to int using LITTLE_ENDIAN");
assertEquals(expectedValue, actualValue, "Failed to support bytes to int using LITTLE_ENDIAN");
}
@Test
@@ -228,6 +228,6 @@ public class ByteUtilTest {
final byte[] bytes = new byte[]{(byte) 0xFF, (byte) 0xFE, (byte) 0x01, (byte) 0x00};
final int expectedValue = -130816;
final int actualValue = ByteUtil.toInt(bytes, 0, ByteOrder.BIG_ENDIAN);
assertEquals(expectedValue, actualValue, "Failed to convert bytes to int using BIG_ENDIAN");
assertEquals(expectedValue, actualValue, "Failed to support bytes to int using BIG_ENDIAN");
}
}