This commit is contained in:
Looly
2023-05-20 00:29:28 +08:00
parent ea1c49a93b
commit 18784f37dd
3 changed files with 143 additions and 105 deletions

View File

@@ -103,6 +103,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
// ---------------------------------------------------------------------- isBlank
/**
* <p>指定字符串数组中,是否包含空字符串。</p>
* <p>如果指定的字符串数组的长度为 0或者其中的任意一个元素是空字符串则返回 true。</p>
@@ -1186,6 +1187,23 @@ public class ArrayUtil extends PrimitiveArrayUtil {
return (E) ArrayWrapper.of(array).get(index);
}
/**
* 获取满足条件的第一个元素
*
* @param array 数组
* @param predicate 条件
* @param <E> 元素类型
* @return 满足条件的第一个元素,未找到返回{@code null}
*/
public static <E> E get(final E[] array, final Predicate<E> predicate) {
for (final E e : array) {
if (predicate.test(e)) {
return e;
}
}
return null;
}
/**
* 获取数组中所有指定位置的元素值,组成新数组
*
@@ -1249,12 +1267,12 @@ public class ArrayUtil extends PrimitiveArrayUtil {
* @param array 数组
* @param beginInclude 开始位置(包括)
* @param endExclude 结束位置(不包括)
* @param <A> 数组类型
* @return 新的数组
* @since 4.0.6
* @param <A> 数组类型
*/
public static <A> A sub(final A array,
final int beginInclude, final int endExclude) {
final int beginInclude, final int endExclude) {
return ArrayWrapper.of(array).getSub(beginInclude, endExclude);
}
@@ -1265,12 +1283,12 @@ public class ArrayUtil extends PrimitiveArrayUtil {
* @param beginInclude 开始位置(包括)
* @param endExclude 结束位置(不包括)
* @param step 步进
* @param <A> 数组类型
* @return 新的数组
* @since 4.0.6
* @param <A> 数组类型
*/
public static <A> A sub(final A array,
final int beginInclude, final int endExclude, final int step) {
final int beginInclude, final int endExclude, final int step) {
return ArrayWrapper.of(array).getSub(beginInclude, endExclude, step);
}

View File

@@ -12,6 +12,7 @@
package org.dromara.hutool.core.bean;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.map.CaseInsensitiveMap;
import org.dromara.hutool.core.reflect.FieldUtil;
@@ -149,7 +150,6 @@ public class BeanDesc implements Serializable {
/**
* 初始化<br>
* 只有与属性关联的相关Getter和Setter方法才会被读取无关的getXXX和setXXX都被忽略
*
*/
private void init() {
final Method[] gettersAndSetters = MethodUtil.getPublicMethods(this.beanClass, MethodUtil::isGetterOrSetterIgnoreCase);
@@ -209,22 +209,57 @@ public class BeanDesc implements Serializable {
final Class<?> fieldType = field.getType();
final boolean isBooleanField = BooleanUtil.isBoolean(fieldType);
// Getter: name -> getName, Setter: name -> setName
final Method[] getterAndSetter = findGetterAndSetter(fieldName, fieldType, gettersOrSetters, ignoreCase);
if (isBooleanField) {
if (null == getterAndSetter[0]) {
// isName -> isName or isIsName
// name -> isName
getterAndSetter[0] = findGetterForBoolean(fieldName, gettersOrSetters, ignoreCase);
}
if (null == getterAndSetter[1]) {
// isName -> setName
getterAndSetter[1] = findSetterForBoolean(fieldName, gettersOrSetters, ignoreCase);
}
}
return new PropDesc(field, getterAndSetter[0], getterAndSetter[1]);
}
/**
* 查找字段对应的Getter和Setter方法<br>
* 此方法不区分是否为boolean字段查找规则为
* <ul>
* <li>Getter要求无参数且返回值是字段类型或字段的父类</li>
* <li>Getter中如果字段为name匹配getName</li>
* <li>Setter要求一个参数且参数必须为字段类型或字段的子类</li>
* <li>Setter中如果字段为name匹配setName</li>
* </ul>
*
* @param fieldName 字段名
* @param fieldType 字段类型
* @param gettersOrSetters 类中所有的Getter或Setter方法
* @return PropDesc
*/
private Method[] findGetterAndSetter(final String fieldName, final Class<?> fieldType,
final Method[] gettersOrSetters, final boolean ignoreCase) {
Method getter = null;
Method setter = null;
String methodName;
for (final Method method : gettersOrSetters) {
methodName = method.getName();
if (method.getParameterCount() == 0) {
if (0 == method.getParameterCount()) {
// 无参数可能为Getter方法
if (isMatchGetter(methodName, fieldName, isBooleanField, ignoreCase)) {
// 方法名与字段名匹配则为Getter方法
if (StrUtil.equals(methodName, StrUtil.genGetter(fieldName), ignoreCase) &&
method.getReturnType().isAssignableFrom(fieldType)) {
// getter的返回类型必须为字段类型或字段的父类
getter = method;
}
} else if (isMatchSetter(methodName, fieldName, isBooleanField, ignoreCase)) {
// setter方法的参数类型和字段类型必须一致或参数类型是字段类型的子类
if(fieldType.isAssignableFrom(method.getParameterTypes()[0])){
setter = method;
}
} else if (StrUtil.equals(methodName, StrUtil.genSetter(fieldName), ignoreCase) &&
fieldType.isAssignableFrom(method.getParameterTypes()[0])) {
// setter方法的参数必须为字段类型或字段的子类
setter = method;
}
if (null != getter && null != setter) {
// 如果Getter和Setter方法都找到了不再继续寻找
@@ -232,104 +267,85 @@ public class BeanDesc implements Serializable {
}
}
return new PropDesc(field, getter, setter);
return new Method[]{getter, setter};
}
/**
* 方法是否为Getter方法<br>
* 匹配规则如下(忽略大小写):
* 针对Boolean或boolean类型字段查找其对应的Getter方法,规则为:
* <ul>
* <li>方法必须无参数且返回boolean或Boolean</li>
* <li>如果字段为isName, 匹配isName、isIsName方法两个方法均存在则按照提供的方法数组优先匹配。</li>
* <li>如果字段为name, 匹配isName方法</li>
* </ul>
* <p>
* 需要注意的是,以下两种格式不匹配,由{@link #findGetterAndSetter(String, Class, Method[], boolean)}完成:
* <ul>
* <li>如果字段为name, 匹配getName</li>
* <li>如果字段为isName, 匹配getIsName</li>
* </ul>
*
* <pre>
* 字段名 -》 方法
* isName -》 isName
* isName -》 isIsName
* isName -》 getIsName
* name -》 isName
* name -》 getName
* </pre>
*
* @param methodName 方法名
* @param fieldName 字段名
* @param isBooleanField 是否为Boolean类型字段
* @param ignoreCase 匹配是否忽略大小写
* @return 是否匹配
* @param fieldName 字段名
* @param gettersOrSetters 所有方法
* @param ignoreCase 是否忽略大小写
* @return 查找到的方法,{@code null}表示未找到
*/
private boolean isMatchGetter(String methodName, String fieldName, final boolean isBooleanField, final boolean ignoreCase) {
final String handledFieldName;
if (ignoreCase) {
// 全部转为小写,忽略大小写比较
methodName = methodName.toLowerCase();
handledFieldName = fieldName.toLowerCase();
fieldName = handledFieldName;
} else {
handledFieldName = StrUtil.upperFirst(fieldName);
}
private Method findGetterForBoolean(final String fieldName, final Method[] gettersOrSetters, final boolean ignoreCase) {
// 查找isXXX
return ArrayUtil.get(gettersOrSetters, m -> {
if (0 != m.getParameterCount() || false == BooleanUtil.isBoolean(m.getReturnType())) {
// getter方法要求无参数且返回boolean或Boolean
return false;
}
// 针对Boolean类型特殊检查
if (isBooleanField) {
if (fieldName.startsWith("is")) {
// 字段已经是is开头
if (methodName.equals(fieldName) // isName -》 isName
|| ("get" + handledFieldName).equals(methodName)// isName -》 getIsName
|| ("is" + handledFieldName).equals(methodName)// isName -》 isIsName
) {
if (StrUtil.startWith(fieldName, "is", ignoreCase)) {
// isName -》 isName
if(StrUtil.equals(fieldName, m.getName(), ignoreCase)){
return true;
}
} else if (("is" + handledFieldName).equals(methodName)) {
// 字段非is开头 name -》 isName
return true;
}
}
// 包括boolean的任何类型只有一种匹配情况name -》 getName
return ("get" + handledFieldName).equals(methodName);
// name -》 isName
// isName -》 isIsName
return StrUtil.equals(StrUtil.upperFirstAndAddPre(fieldName, "is"), m.getName(), ignoreCase);
});
}
/**
* 方法是否为Setter方法<br>
* 匹配规则如下(忽略大小写):
* 针对Boolean或boolean类型字段查找其对应的Setter方法,规则为:
* <ul>
* <li>方法必须为1个boolean或Boolean参数</li>
* <li>如果字段为isName匹配setName</li>
* </ul>
* <p>
* 需要注意的是,以下两种格式不匹配,由{@link #findGetterAndSetter(String, Class, Method[], boolean)}完成:
* <ul>
* <li>如果字段为name, 匹配setName</li>
* <li>如果字段为isName, 匹配setIsName</li>
* </ul>
*
* <pre>
* 字段名 -》 方法
* isName -》 setName
* isName -》 setIsName
* name -》 setName
* </pre>
*
* @param methodName 方法名
* @param fieldName 字段名
* @param isBooleanField 是否为Boolean类型字段
* @param ignoreCase 匹配是否忽略大小写
* @return 是否匹配
* @param fieldName 字段名
* @param gettersOrSetters 所有方法
* @param ignoreCase 是否忽略大小写
* @return 查找到的方法,{@code null}表示未找到
*/
private boolean isMatchSetter(String methodName, String fieldName, final boolean isBooleanField, final boolean ignoreCase) {
final String handledFieldName;
if (ignoreCase) {
// 全部转为小写,忽略大小写比较
methodName = methodName.toLowerCase();
handledFieldName = fieldName.toLowerCase();
fieldName = handledFieldName;
} else {
handledFieldName = StrUtil.upperFirst(fieldName);
}
// 非标准Setter方法跳过
if (!methodName.startsWith("set")) {
return false;
}
// 针对Boolean类型特殊检查
if (isBooleanField && fieldName.startsWith("is")) {
// 字段是is开头
if (("set" + StrUtil.removePrefix(fieldName, "is")).equals(methodName)// isName -》 setName
|| ("set" + handledFieldName).equals(methodName)// isName -》 setIsName
) {
return true;
private Method findSetterForBoolean(final String fieldName, final Method[] gettersOrSetters, final boolean ignoreCase) {
// 查找isXXX
return ArrayUtil.get(gettersOrSetters, m -> {
if (1 != m.getParameterCount() || false == BooleanUtil.isBoolean(m.getParameterTypes()[0])) {
// setter方法要求1个boolean或Boolean参数
return false;
}
}
// 包括boolean的任何类型只有一种匹配情况name -》 setName
return ("set" + handledFieldName).equals(methodName);
if (StrUtil.startWith(fieldName, "is", ignoreCase)) {
// isName -》 setName
return StrUtil.equals(
"set" + StrUtil.removePrefix(fieldName, "is", ignoreCase),
m.getName(), ignoreCase);
}
// 其它不匹配
return false;
});
}
// ------------------------------------------------------------------------------------------------------ Private method end
}

View File

@@ -1113,18 +1113,10 @@ public class CharSequenceUtil extends StrValidator {
*
* @param str 字符串
* @param prefix 前缀
* @return 切掉后的字符串,若前缀不是 preffix 返回原字符串
* @return 切掉后的字符串,若前缀不是 prefix 返回原字符串
*/
public static String removePrefix(final CharSequence str, final CharSequence prefix) {
if (isEmpty(str) || isEmpty(prefix)) {
return str(str);
}
final String str2 = str.toString();
if (str2.startsWith(prefix.toString())) {
return subSuf(str2, prefix.length());// 截取后半段
}
return str2;
return removePrefix(str, prefix, false);
}
/**
@@ -1135,12 +1127,24 @@ public class CharSequenceUtil extends StrValidator {
* @return 切掉后的字符串,若前缀不是 prefix 返回原字符串
*/
public static String removePrefixIgnoreCase(final CharSequence str, final CharSequence prefix) {
return removePrefix(str, prefix, true);
}
/**
* 去掉指定前缀
*
* @param str 字符串
* @param prefix 前缀
* @param ignoreCase 是否忽略大小写
* @return 切掉后的字符串,若前缀不是 prefix 返回原字符串
*/
public static String removePrefix(final CharSequence str, final CharSequence prefix, final boolean ignoreCase) {
if (isEmpty(str) || isEmpty(prefix)) {
return str(str);
}
final String str2 = str.toString();
if (startWithIgnoreCase(str, prefix)) {
if (startWith(str, prefix, ignoreCase)) {
return subSuf(str2, prefix.length());// 截取后半段
}
return str2;