Merge remote-tracking branch 'origin/v6-dev' into v6-dev

# Conflicts:
#	hutool-core/src/main/java/cn/hutool/core/stream/EasyStream.java
This commit is contained in:
VampireAchao 2022-10-07 11:41:12 +08:00
commit 2c2ad12972
337 changed files with 6044 additions and 4068 deletions

View File

@ -1,10 +1,19 @@
### 说明
### 📣说明
1. 请确认你提交的PR是到'v6-dev'分支否则我会手动修改代码并关闭PR。
2. 请确认没有更改代码风格如tab缩进
3. 新特性添加请确认注释完备如有必要请在src/test/java下添加Junit测试用例
### 修改描述(包括说明bug修复或者添加新特性)
### 🔧修改描述(包括说明bug修复或者添加新特性)
1. [bug修复] balabala……
2. [新特性] balabala……
2. [新特性] balabala……
### 😊提交前自测
> 请在提交前自测确保代码没有问题,提交新代码应包含:测试用例、通过(mvn javadoc:javadoc)检验详细注释。
1. 本地如有多个JDK版本可以设置临时JDk版本,如:`export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_331.jdk/Contents/Home`具体替换为本地jdk目录
2. 确保本地测试使用JDK8最新版本`echo $JAVA_HOME``mvn -v``java -version`均正确。
3. 执行打包生成文档,使用`mvn clean package -Dmaven.test.skip=true -U`,并确认通过,会自动执行打包、生成文档
4. 如需要单独执行文档生成,执行:`mvn javadoc:javadoc `,并确认通过
5. 如需要单独执行测试用例,执行:`mvn clean test`,并确认通过

View File

@ -1,10 +1,19 @@
#### 说明
### 📣说明
1. 请确认你提交的PR是到'v5-dev'分支否则我会手动修改代码并关闭PR。
2. 请确认没有更改代码风格如tab缩进
3. 新特性添加请确认注释完备如有必要请在src/test/java下添加Junit测试用例
### 修改描述(包括说明bug修复或者添加新特性)
### 🔧修改描述(包括说明bug修复或者添加新特性)
1. [bug修复] balabala……
2. [新特性] balabala……
2. [新特性] balabala……
### 😊提交前自测
> 请在提交前自测确保代码没有问题,提交新代码应包含:测试用例、通过(mvn javadoc:javadoc)检验详细注释。
1. 本地如有多个JDK版本可以设置临时JDk版本,如:`export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_331.jdk/Contents/Home`具体替换为本地jdk目录
2. 确保本地测试使用JDK8最新版本`echo $JAVA_HOME``mvn -v``java -version`均正确。
3. 执行打包生成文档,使用`mvn clean package -Dmaven.test.skip=true -U`,并确认通过,会自动执行打包、生成文档
4. 如需要单独执行文档生成,执行:`mvn javadoc:javadoc `,并确认通过
5. 如需要单独执行测试用例,执行:`mvn clean test`,并确认通过

View File

@ -93,11 +93,41 @@ import java.util.stream.Stream;
* </li>
* </ul>
*
* <p><strong>可重复注解支持</strong>
* <p>工具类中格式为<em>findAllXXX</em><em>getAllXXX</em>格式的方法
* 支持获得{@link AnnotatedElement}上的可重复注解
* 此处的可重复注解定义包括两方面
* <ul>
* <li>
* {@link AnnotatedElement}存在直接声明的注解该注解有且仅有一个<em>value</em>属性
* 该属性类型为注解数组且数组中注解被{@link java.lang.annotation.Repeatable}注解
* 则认为被包括的注解为可重复注解<br>
* eg:<br>
* A上存在注解<em>X</em>该注解是一个容器注解内部包含可重复注解<em>Y</em>
* 解析<em>X</em>得到注解<em>X</em>与它包含的可重复注解<em>Y</em>
* </li>
* <li>
* {@link AnnotatedElement}存在直接声明的注解该注解与其他根注解皆有相同的元注解
* 则获得元注解时可以获得多个该相同的元注解<br>
* eg:<br>
* A上存在注解<em>X</em><em>Y</em>两者皆有元注解<em>Z</em>
* 则通过{@link AnnotatedElement}可以获得两个<em>Z</em>
* </li>
* </ul>
*
* <p><strong>缓存</strong>
* <p>为了避免注解以及{@link AnnotatedElement}层级结构解析过程中的大量反射调用
* 工具类为{@link AnnotatedElement}及其元注解信息进行了缓存<br>
* 缓存功能默认基于{@link WeakConcurrentMap}实现会在gc时自动回收部分缓存数据
* 但是若有必要也可以调用{@link #clearCaches()}方法主动清空缓存
*
* @author huangchengxing
* @see ResolvedAnnotationMapping
* @see GenericAnnotationMapping
* @see HierarchicalAnnotatedElements
* @see RepeatableMetaAnnotatedElement
* @see MetaAnnotatedElement
* @see RepeatableAnnotationCollector
* @since 6.0.0
*/
public class AnnotatedElementUtil {
@ -112,6 +142,16 @@ public class AnnotatedElementUtil {
*/
private static final Map<AnnotatedElement, MetaAnnotatedElement<GenericAnnotationMapping>> ELEMENT_CACHE = new WeakConcurrentMap<>();
/**
* 不支持属性解析的{@link RepeatableMetaAnnotatedElement}缓存
*/
private static final Map<AnnotatedElement, RepeatableMetaAnnotatedElement<ResolvedAnnotationMapping>> RESOLVED_REPEATABLE_ELEMENT_CACHE = new WeakConcurrentMap<>();
/**
* 不支持属性解析的{@link RepeatableMetaAnnotatedElement}缓存
*/
private static final Map<AnnotatedElement, RepeatableMetaAnnotatedElement<GenericAnnotationMapping>> REPEATABLE_ELEMENT_CACHE = new WeakConcurrentMap<>();
// region ========== find ==========
/**
@ -140,7 +180,8 @@ public class AnnotatedElementUtil {
}
/**
* {@code element}所处层级结构的所有{@link AnnotatedElement}获取所有该类型的注解或元注解
* {@code element}所处层级结构的所有{@link AnnotatedElement}上直接声明的注解
* 这些注解包含的可重复注解以及上述所有注解的元注解中获取指定类型注解
*
* @param element {@link AnnotatedElement}
* @param annotationType 注解类型
@ -148,7 +189,7 @@ public class AnnotatedElementUtil {
* @return 注解对象
*/
public static <T extends Annotation> T[] findAllAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
return toHierarchyMetaElement(element, false)
return toHierarchyRepeatableMetaElement(element, false)
.getAnnotationsByType(annotationType);
}
@ -190,7 +231,8 @@ public class AnnotatedElementUtil {
}
/**
* {@code element}所处层级结构的所有{@link AnnotatedElement}获取所有该类型的注解或元注解<br>
* {@code element}所处层级结构的所有{@link AnnotatedElement}上直接声明的注解
* 这些注解包含的可重复注解以及上述所有注解的元注解中获取指定类型注解<br>
* 得到的注解支持基于{@link Alias}的别名及子注解对元注解中同名同类型属性进行覆写的特殊机制
*
* @param element {@link AnnotatedElement}
@ -199,7 +241,7 @@ public class AnnotatedElementUtil {
* @return 注解对象
*/
public static <T extends Annotation> T[] findAllResolvedAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
return toHierarchyMetaElement(element, true)
return toHierarchyRepeatableMetaElement(element, true)
.getAnnotationsByType(annotationType);
}
@ -221,7 +263,8 @@ public class AnnotatedElementUtil {
}
/**
* {@code element}所处层级结构的所有{@link AnnotatedElement}上获取所有该类型的注解
* {@code element}上直接声明的注解这些注解包含的可重复注解
* 以及上述所有注解的元注解中获取指定类型注解
*
* @param element {@link AnnotatedElement}
* @param annotationType 注解类型
@ -229,7 +272,7 @@ public class AnnotatedElementUtil {
* @return 注解对象
*/
public static <T extends Annotation> T[] findAllDirectlyAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
return toHierarchyMetaElement(element, false)
return toHierarchyRepeatableMetaElement(element, false)
.getDeclaredAnnotationsByType(annotationType);
}
@ -271,7 +314,8 @@ public class AnnotatedElementUtil {
}
/**
* {@code element}所处层级结构的所有{@link AnnotatedElement}上获取所有该类型的注解<br>
* {@code element}所处层级结构的所有{@link AnnotatedElement}上直接声明的注解
* 这些注解包含的可重复注解以及上述所有注解的元注解中获取指定类型注解<br>
* 得到的注解支持基于{@link Alias}的别名及子注解对元注解中同名同类型属性进行覆写的特殊机制
*
* @param element {@link AnnotatedElement}
@ -280,7 +324,7 @@ public class AnnotatedElementUtil {
* @return 注解对象
*/
public static <T extends Annotation> T[] findAllDirectlyResolvedAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
return toHierarchyMetaElement(element, true)
return toHierarchyRepeatableMetaElement(element, true)
.getDeclaredAnnotationsByType(annotationType);
}
@ -324,6 +368,19 @@ public class AnnotatedElementUtil {
.getAnnotations();
}
/**
* {@code element}上直接声明的注解这些注解包含的可重复注解以及上述所有注解的元注解中获取指定类型注解
*
* @param element {@link AnnotatedElement}
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 注解对象
*/
public static <T extends Annotation> T[] getAllAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
return toRepeatableMetaElement(element, false)
.getAnnotationsByType(annotationType);
}
/**
* {@code element}获取所有的注解或元注解<br>
* 得到的注解支持基于{@link Alias}的别名机制
@ -350,6 +407,20 @@ public class AnnotatedElementUtil {
.getAnnotations();
}
/**
* {@code element}上直接声明的注解这些注解包含的可重复注解以及上述所有注解的元注解中获取指定类型注解<br>
* 得到的注解支持基于{@link Alias}的别名机制
*
* @param element {@link AnnotatedElement}
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 注解对象
*/
public static <T extends Annotation> T[] getAllResolvedAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
return toRepeatableMetaElement(element, true)
.getAnnotationsByType(annotationType);
}
// endregion
// region ========== get & direct ==========
@ -367,6 +438,19 @@ public class AnnotatedElementUtil {
.getDeclaredAnnotation(annotationType);
}
/**
* {@code element}上直接声明的注解这些注解包含的可重复注解中获取指定类型注解
*
* @param element {@link AnnotatedElement}
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 注解对象
*/
public static <T extends Annotation> T[] getAllDirectlyAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
return toRepeatableMetaElement(element, false)
.getDeclaredAnnotationsByType(annotationType);
}
/**
* {@code element}上获取所有的注解
*
@ -404,6 +488,19 @@ public class AnnotatedElementUtil {
.getDeclaredAnnotations();
}
/**
* {@code element}上直接声明的注解这些注解包含的可重复注解中获取指定类型注解
*
* @param element {@link AnnotatedElement}
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 注解对象
*/
public static <T extends Annotation> T[] getAllDirectlyResolvedAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
return toRepeatableMetaElement(element, true)
.getDeclaredAnnotationsByType(annotationType);
}
// endregion
// region ========== to element ==========
@ -430,6 +527,29 @@ public class AnnotatedElementUtil {
return HierarchicalAnnotatedElements.create(element, (es, e) -> getMetaElementCache(e));
}
/**
* <p>扫描{@code element}所处层级结构中的{@link AnnotatedElement}
* 并将其全部转为{@link RepeatableMetaAnnotatedElement}
* 再把所有对象合并为{@link HierarchicalAnnotatedElements}<br>
* 得到的对象可访问{@code element}所处层级结构中所有{@link AnnotatedElement}上的直接声明的注解
* 这些注解包含的可重复注解以及上述注解的所有元注解
*
* @param element 元素
* @param resolved 是否解析注解属性若为{@code true}则获得的注解将支持属性别名以及属性覆盖机制
* @return {@link HierarchicalAnnotatedElements}实例
* @see #getRepeatableMetaElementCache(AnnotatedElement)
* @see #getResolvedRepeatableMetaElementCache(AnnotatedElement)
*/
public static AnnotatedElement toHierarchyRepeatableMetaElement(final AnnotatedElement element, final boolean resolved) {
if (Objects.isNull(element)) {
return emptyElement();
}
if (resolved) {
return HierarchicalAnnotatedElements.create(element, (es, e) -> getResolvedRepeatableMetaElementCache(e));
}
return HierarchicalAnnotatedElements.create(element, (es, e) -> getRepeatableMetaElementCache(e));
}
/**
* <p>扫描{@code element}所处层级结构中的{@link AnnotatedElement}
* 再把所有对象合并为{@link HierarchicalAnnotatedElements}
@ -460,6 +580,49 @@ public class AnnotatedElementUtil {
);
}
/**
* {@link AnnotatedElement}转为{@link RepeatableMetaAnnotatedElement}
* 得到的对象可访问{@link AnnotatedElement}上的直接声明的注解这些注解包含的可重复注解以及上述注解的所有元注解
*
* @param element 元素
* @param resolved 是否解析注解属性若为{@code true}则获得的注解将支持属性别名以及属性覆盖机制
* @return {@link AnnotatedElement}实例
* @see #getMetaElementCache(AnnotatedElement)
* @see #getResolvedMetaElementCache(AnnotatedElement)
*/
public static AnnotatedElement toRepeatableMetaElement(final AnnotatedElement element, final boolean resolved) {
return ObjUtil.defaultIfNull(
element, e -> resolved ? getResolvedRepeatableMetaElementCache(e) : getRepeatableMetaElementCache(e), emptyElement()
);
}
/**
* <p>{@link AnnotatedElement}转为{@link RepeatableMetaAnnotatedElement}
* 得到的对象可访问{@link AnnotatedElement}上的直接声明的注解
* 通过{@code collector}从这些注解获得的可重复注解以及上述注解的所有元注解<br>
* 注意方法将不会通过缓存结果因此每次调用都需要重新通过反射并获得相关注解
*
* @param collector 可重复注解收集器{@code null}时等同于{@link RepeatableAnnotationCollector#none()}
* @param element 元素
* @param resolved 是否解析注解属性若为{@code true}则获得的注解将支持属性别名以及属性覆盖机制
* @return {@link AnnotatedElement}实例
*/
public static AnnotatedElement toRepeatableMetaElement(
final AnnotatedElement element, RepeatableAnnotationCollector collector, final boolean resolved) {
if (Objects.isNull(element)) {
return emptyElement();
}
collector = ObjUtil.defaultIfNull(collector, RepeatableAnnotationCollector.none());
if (resolved) {
return RepeatableMetaAnnotatedElement.create(
collector, element, (source, annotation) -> ResolvedAnnotationMapping.create((ResolvedAnnotationMapping)source, annotation, true)
);
}
return RepeatableMetaAnnotatedElement.create(
collector, element, (source, annotation) -> GenericAnnotationMapping.create(annotation, Objects.isNull(source))
);
}
/**
* 将一组注解中的非{@code null}注解对象合并为一个{@link AnnotatedElement}
*
@ -493,26 +656,70 @@ public class AnnotatedElementUtil {
* @param element {@link AnnotatedElement}
* @return {@link MetaAnnotatedElement}实例
*/
private static MetaAnnotatedElement<ResolvedAnnotationMapping> getResolvedMetaElementCache(final AnnotatedElement element) {
static MetaAnnotatedElement<ResolvedAnnotationMapping> getResolvedMetaElementCache(final AnnotatedElement element) {
return RESOLVED_ELEMENT_CACHE.computeIfAbsent(element, ele -> MetaAnnotatedElement.create(
element, (source, annotation) -> ResolvedAnnotationMapping.create(source, annotation, true)
));
}
/**
* 创建一个支持注解解析的{@link MetaAnnotatedElement}
* 创建一个支持注解解析的{@link MetaAnnotatedElement}
*
* @param element {@link AnnotatedElement}
* @return {@link MetaAnnotatedElement}实例
*/
private static MetaAnnotatedElement<GenericAnnotationMapping> getMetaElementCache(final AnnotatedElement element) {
static MetaAnnotatedElement<GenericAnnotationMapping> getMetaElementCache(final AnnotatedElement element) {
return ELEMENT_CACHE.computeIfAbsent(element, ele -> MetaAnnotatedElement.create(
element, (source, annotation) -> GenericAnnotationMapping.create(annotation, Objects.isNull(source))
));
}
/**
* 创建一个支持注解解析的{@link RepeatableMetaAnnotatedElement}
*
* @param element {@link AnnotatedElement}
* @return {@link MetaAnnotatedElement}实例
*/
static RepeatableMetaAnnotatedElement<ResolvedAnnotationMapping> getResolvedRepeatableMetaElementCache(final AnnotatedElement element) {
return RESOLVED_REPEATABLE_ELEMENT_CACHE.computeIfAbsent(element, ele -> RepeatableMetaAnnotatedElement.create(
element, (source, annotation) -> ResolvedAnnotationMapping.create(source, annotation, true)
));
}
/**
* 创建一个不支持注解解析的{@link RepeatableMetaAnnotatedElement}
*
* @param element {@link AnnotatedElement}
* @return {@link MetaAnnotatedElement}实例
*/
static RepeatableMetaAnnotatedElement<GenericAnnotationMapping> getRepeatableMetaElementCache(final AnnotatedElement element) {
return REPEATABLE_ELEMENT_CACHE.computeIfAbsent(element, ele -> RepeatableMetaAnnotatedElement.create(
element, (source, annotation) -> GenericAnnotationMapping.create(annotation, Objects.isNull(source))
));
}
// endregion
/**
* 清空相关缓存包括
* <ul>
* <li>{@link AnnotatedElementUtil}中的{@link AnnotatedElement}{@link AnnotationMapping}缓存</li>
* <li>{@link AnnotationUtil}中的{@link AnnotatedElement}上直接声明的注解缓存</li>
* <li>{@link RepeatableAnnotationCollector}中单例的注解属性缓存</li>
* </ul>
*
* @see AnnotationUtil#clearCaches()
* @see RepeatableAnnotationCollector#clearSingletonCaches()
*/
public static void clearCaches() {
ELEMENT_CACHE.clear();
RESOLVED_ELEMENT_CACHE.clear();
REPEATABLE_ELEMENT_CACHE.clear();
RESOLVED_REPEATABLE_ELEMENT_CACHE.clear();
RepeatableAnnotationCollector.clearSingletonCaches();
AnnotationUtil.clearCaches();
}
/**
* 由一组注解聚合来的{@link AnnotatedElement}
*/

View File

@ -1,19 +1,31 @@
package cn.hutool.core.annotation;
import cn.hutool.core.classloader.ClassLoaderUtil;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.func.LambdaInfo;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.lang.func.SerFunction;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.reflect.FieldUtil;
import cn.hutool.core.reflect.MethodUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;
import java.lang.annotation.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Stream;
@ -26,6 +38,22 @@ import java.util.stream.Stream;
*/
public class AnnotationUtil {
/**
* 直接声明的注解缓存
*/
private static final Map<AnnotatedElement, Annotation[]> DECLARED_ANNOTATIONS_CACHE = new WeakConcurrentMap<>();
/**
* 获取直接声明的注解若已有缓存则从缓存中获取
*
* @param element {@link AnnotatedElement}
* @return 注解
* @since 6.0.0
*/
public static Annotation[] getDeclaredAnnotations(final AnnotatedElement element) {
return MapUtil.computeIfAbsent(DECLARED_ANNOTATIONS_CACHE, element, AnnotatedElement::getDeclaredAnnotations);
}
/**
* 将指定的被注解的元素转换为组合注解元素
*
@ -151,6 +179,28 @@ public class AnnotationUtil {
return getAnnotationValue(annotationEle, annotationType, "value");
}
/**
* 获取指定注解属性的值<br>
* 如果无指定的属性方法返回null
*
* @param <A> 注解类型
* @param <R> 注解类型值
* @param annotationEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @param propertyName 属性名例如注解中定义了name()方法 此处传入name
* @return 注解对象
* @throws UtilException 调用注解中的方法时执行异常
*/
public static <A extends Annotation, R> R getAnnotationValue(final AnnotatedElement annotationEle, final SerFunction<A, R> propertyName) {
if(propertyName == null) {
return null;
}else {
final LambdaInfo lambda = LambdaUtil.resolve(propertyName);
final String instantiatedMethodType = lambda.getLambda().getInstantiatedMethodType();
final Class<A> annotationClass = ClassLoaderUtil.loadClass(StrUtil.sub(instantiatedMethodType, 2, StrUtil.indexOf(instantiatedMethodType, ';')));
return getAnnotationValue(annotationEle,annotationClass, lambda.getLambda().getImplMethodName());
}
}
/**
* 获取指定注解属性的值<br>
* 如果无指定的属性方法返回null
@ -295,16 +345,14 @@ public class AnnotationUtil {
}
/**
* 获取注解属性
* 获取注解属性若已有缓存则从缓存中获取
*
* @param annotationType 注解类型
* @return 注解属性
* @since 6.0.0
*/
public static Method[] getAnnotationAttributes(final Class<? extends Annotation> annotationType) {
// TODO 改为通过带缓存的反射工具类完成
Objects.requireNonNull(annotationType);
return Stream.of(annotationType.getDeclaredMethods())
return Stream.of(MethodUtil.getDeclaredMethods(annotationType))
.filter(AnnotationUtil::isAnnotationAttribute)
.toArray(Method[]::new);
}
@ -339,4 +387,11 @@ public class AnnotationUtil {
&& !attribute.isSynthetic();
}
/**
* 清空相关缓存
*/
public static void clearCaches() {
DECLARED_ANNOTATIONS_CACHE.clear();
}
}

View File

@ -4,11 +4,7 @@ import cn.hutool.core.collection.SetUtil;
import cn.hutool.core.map.TableMap;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.*;
import java.lang.reflect.AnnotatedElement;
import java.util.Arrays;
import java.util.Collection;
@ -115,7 +111,7 @@ public class CombinationAnnotationElement implements AnnotatedElement, Serializa
* @param element 元素
*/
private void init(final AnnotatedElement element) {
final Annotation[] declaredAnnotations = element.getDeclaredAnnotations();
final Annotation[] declaredAnnotations = AnnotationUtil.getDeclaredAnnotations(element);
this.declaredAnnotationMap = new TableMap<>();
parseDeclared(declaredAnnotations);
@ -145,7 +141,7 @@ public class CombinationAnnotationElement implements AnnotatedElement, Serializa
declaredAnnotationMap.put(annotationType, annotation);
}
// 测试不通过的注解不影响继续递归
parseDeclared(annotationType.getDeclaredAnnotations());
parseDeclared(AnnotationUtil.getDeclaredAnnotations(annotationType));
}
}
}

View File

@ -2,6 +2,7 @@ package cn.hutool.core.annotation;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.reflect.ClassUtil;
import cn.hutool.core.reflect.MethodUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ArrayUtil;
@ -19,7 +20,7 @@ import java.util.stream.Stream;
* 并将其包装为{@link MetaAnnotatedElement} <br>
* eg: <br>
* 若存在元素<em>A</em>有对应父类与父接口<em>B</em><em>C</em>
* 则根据<em>A</em>生成的{@link HierarchicalAnnotatedElements}实例将同时包含<em>A</em><em>B</em><em>C</em>,
* 则根据<em>A</em>生成的{@code HierarchicalAnnotatedElements}实例将同时包含<em>A</em><em>B</em><em>C</em>,
* 该实例同时支持对这三个实例上直接声明的注解以及这些注解的元注解进行访问
*
* <p><strong>注解搜索范围</strong>
@ -32,7 +33,7 @@ import java.util.stream.Stream;
* <li>被保存的所有{@link AnnotatedElement}上直接声明的注解及这些注解的元注解</li>
* <li>若是类则包括其所有父类和所有父接口上声明的注解和元注解</li>
* <li>
* 若是方法且不是静态/私有/<code>final</code>修饰的方法时
* 若是方法且不是静态/私有/{@code final}修饰的方法时
* 则额外获取包括其声明类的所有父类和所有父接口中与该方法具有相同方法签名的方法上的注解和元注解
* </li>
* </ol>
@ -69,9 +70,9 @@ public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
/**
* 创建一个分层注解元素
*
* @param element 被包装的元素若元素已是{@link HierarchicalAnnotatedElements}则返回其本身
* @return {@link HierarchicalAnnotatedElements}实例
* {@code element}也是一个{@link HierarchicalAnnotatedElements}返回{@code element}本身
* @param element 被包装的元素若元素已是{@code HierarchicalAnnotatedElements}则返回其本身
* @return {@code HierarchicalAnnotatedElements}实例
* {@code element}也是一个{@code HierarchicalAnnotatedElements}返回{@code element}本身
*/
public static HierarchicalAnnotatedElements create(final AnnotatedElement element) {
return create(element, (es, e) -> e);
@ -80,10 +81,10 @@ public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
/**
* 创建一个分层注解元素
*
* @param element 被包装的元素若元素已是{@link HierarchicalAnnotatedElements}则返回其本身
* @param element 被包装的元素若元素已是{@code HierarchicalAnnotatedElements}则返回其本身
* @param elementFactory 创建{@link AnnotatedElement}的工厂方法当返回{@code null}时将忽略该元素
* @return {@link HierarchicalAnnotatedElements}实例
* {@code element}也是一个{@link HierarchicalAnnotatedElements}返回{@code element}本身
* @return {@code HierarchicalAnnotatedElements}实例
* {@code element}也是一个{@code HierarchicalAnnotatedElements}返回{@code element}本身
*/
public static HierarchicalAnnotatedElements create(
final AnnotatedElement element,
@ -156,8 +157,6 @@ public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
* @param <A> 注解类型
* @return 注解对象
*/
@SuppressWarnings("unchecked")
@Override
public <A extends Annotation> A[] getAnnotationsByType(final Class<A> annotationType) {
return getElementMappings().stream()
.map(e -> e.getAnnotationsByType(annotationType))
@ -174,7 +173,7 @@ public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
@Override
public Annotation[] getDeclaredAnnotations() {
return getElementMappings().stream()
.map(AnnotatedElement::getDeclaredAnnotations)
.map(AnnotationUtil::getDeclaredAnnotations)
.filter(ArrayUtil::isNotEmpty)
.flatMap(Stream::of)
.toArray(Annotation[]::new);
@ -239,14 +238,14 @@ public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
* @return 是否
*/
@Override
public boolean equals(Object o) {
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
HierarchicalAnnotatedElements that = (HierarchicalAnnotatedElements)o;
final HierarchicalAnnotatedElements that = (HierarchicalAnnotatedElements)o;
return elementFactory.equals(that.elementFactory) && source.equals(that.source);
}
@ -294,8 +293,8 @@ public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
/**
* 将元素转为{@link MetaAnnotatedElement}后添加至{@code mappings}
*/
private void collectElement(Set<AnnotatedElement> elements, final AnnotatedElement element) {
AnnotatedElement target = elementFactory.apply(elements, element);
private void collectElement(final Set<AnnotatedElement> elements, final AnnotatedElement element) {
final AnnotatedElement target = elementFactory.apply(elements, element);
if (Objects.nonNull(target)) {
elements.add(target);
}
@ -309,7 +308,7 @@ public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
if (Objects.isNull(elementMappings)) {
synchronized (this) {
if (Objects.isNull(elementMappings)) {
Set<AnnotatedElement> mappings = initElementMappings();
final Set<AnnotatedElement> mappings = initElementMappings();
elementMappings = Collections.unmodifiableSet(mappings);
}
}
@ -320,7 +319,7 @@ public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
* 遍历层级结构获取层级结构中所有关联的{@link AnnotatedElement}并添加到{@link #elementMappings}
*/
private Set<AnnotatedElement> initElementMappings() {
Set<AnnotatedElement> mappings = new LinkedHashSet<>();
final Set<AnnotatedElement> mappings = new LinkedHashSet<>();
// 原始元素是类
if (source instanceof Class) {
scanHierarchy(mappings, (Class<?>)source, false, source);
@ -344,8 +343,8 @@ public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
* 按广度优先遍历{@code type}的父类以及父接口并从类上/类中指定方法上获得所需的注解
*/
private void scanHierarchy(
Set<AnnotatedElement> mappings, Class<?> type, final boolean isMethod, final AnnotatedElement source) {
Method methodSource = isMethod ? (Method)source : null;
final Set<AnnotatedElement> mappings, Class<?> type, final boolean isMethod, final AnnotatedElement source) {
final Method methodSource = isMethod ? (Method)source : null;
final Deque<Class<?>> deque = new LinkedList<>();
deque.addLast(type);
final Set<Class<?>> accessed = new HashSet<>();
@ -359,8 +358,7 @@ public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
if (!isMethod) {
collectElement(mappings, type);
} else {
// TODO 改为通过带缓存的反射工具类完成
Stream.of(type.getDeclaredMethods())
Stream.of(MethodUtil.getDeclaredMethods(type))
.filter(method -> isMatchMethod(methodSource, method))
.forEach(method -> collectElement(mappings, method));
}
@ -379,7 +377,7 @@ public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
* <li>该类不为{@link Object}</li>
* </ul>
*/
private boolean isNeedMapping(Class<?> type, Set<Class<?>> accessedTypes) {
private boolean isNeedMapping(final Class<?> type, final Set<Class<?>> accessedTypes) {
return Objects.nonNull(type)
&& !accessedTypes.contains(type)
&& !Objects.equals(type, Object.class);

View File

@ -10,7 +10,6 @@ import java.lang.annotation.Inherited;
import java.lang.reflect.AnnotatedElement;
import java.util.*;
import java.util.function.BiFunction;
import java.util.stream.Stream;
/**
* <p>注解元素映射用于包装一个{@link AnnotatedElement}然后将被包装的元素上
@ -21,8 +20,11 @@ import java.util.stream.Stream;
* 并且在当前实例中{@link Inherited}注解将不生效
* 即通过<em>directly</em>方法将无法获得父类上带有{@link Inherited}的注解
*
* <p>当通过静态工厂方法创建时该实例与关联的{@link ResolvedAnnotationMapping}都会针对{@link ResolvedAnnotationMapping}进行缓存
* 从而避免频繁的反射与代理造成不必要的性能损耗
* <p>在一个{@link MetaAnnotatedElement}
* {@link AnnotatedElement}上同类型的注解或元注解只会被保留一个
* 即当出现两个根注解都具有相同元注解时仅有第一个根注解上的元注解会被保留
* 因此当通过{@link #getAnnotationsByType(Class)}
* {@link #getDeclaredAnnotationsByType(Class)}方法用于只能获得一个注解对象
*
* @author huangchengxing
* @see ResolvedAnnotationMapping
@ -214,6 +216,34 @@ public class MetaAnnotatedElement<T extends AnnotationMapping<Annotation>> imple
return getAnnotationMappings().values().iterator();
}
/**
* 比较两个实例是否相等
*
* @param o 对象
* @return 是否
*/
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final MetaAnnotatedElement<?> that = (MetaAnnotatedElement<?>)o;
return element.equals(that.element) && mappingFactory.equals(that.mappingFactory);
}
/**
* 获取实例的哈希值
*
* @return 哈希值
*/
@Override
public int hashCode() {
return Objects.hash(element, mappingFactory);
}
// ========================= protected =========================
/**
@ -270,7 +300,7 @@ public class MetaAnnotatedElement<T extends AnnotationMapping<Annotation>> imple
*/
private void initAnnotationMappings(final Map<Class<? extends Annotation>, T> mappings) {
final Deque<T> deque = new LinkedList<>();
Arrays.stream(element.getDeclaredAnnotations())
Arrays.stream(AnnotationUtil.getDeclaredAnnotations(element))
.filter(m -> isNeedMapping(mappings, m))
.map(annotation -> createMapping(null, annotation))
.filter(Objects::nonNull)
@ -283,40 +313,15 @@ public class MetaAnnotatedElement<T extends AnnotationMapping<Annotation>> imple
}
// 保存该注解并将其需要处理的元注解也加入队列
mappings.put(mapping.annotationType(), mapping);
Stream.of(mapping.annotationType().getDeclaredAnnotations())
.map(annotation -> createMapping(mapping, annotation))
.filter(Objects::nonNull)
.filter(m -> isNeedMapping(mappings, m))
.forEach(deque::addLast);
for (final Annotation annotation : AnnotationUtil.getDeclaredAnnotations(mapping.annotationType())) {
if (mappings.containsKey(annotation.annotationType())) {
continue;
}
final T m = createMapping(mapping, annotation);
if (Objects.nonNull(m) && isNeedMapping(mappings, m)) {
deque.addLast(m);
}
}
}
}
/**
* 比较两个实例是否相等
*
* @param o 对象
* @return 是否
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MetaAnnotatedElement<?> that = (MetaAnnotatedElement<?>)o;
return element.equals(that.element) && mappingFactory.equals(that.mappingFactory);
}
/**
* 获取实例的哈希值
*
* @return 哈希值
*/
@Override
public int hashCode() {
return Objects.hash(element, mappingFactory);
}
}

View File

@ -0,0 +1,494 @@
package cn.hutool.core.annotation;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.reflect.MethodUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ArrayUtil;
import java.lang.annotation.Annotation;
import java.lang.annotation.Repeatable;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 可重复注解收集器用于从一个注解获得被它包含的可重复注解
*
* @author huangchengxing
*/
public interface RepeatableAnnotationCollector {
/**
* 空实现
*
* @return {@link RepeatableAnnotationCollector}实例
*/
static RepeatableAnnotationCollector none() {
return None.INSTANCE;
}
/**
* <p>当注解中有且仅有一个名为{@code value}的属性时
* 若该属性类型为注解数组且该数组对应的注解类型被{@link Repeatable}注解
* 则收集器将返回该属性中包括的可重复注解<br>
* eg:
* <pre><code>
* // 容器注解
* {@literal @}interface Annotation {
* Item[] value() default {};
* }
* // 可重复注解
* {@literal @}Repeatable(Annotation.class)
* {@literal @}interface Item {}
* </code></pre>
* 解析任意{@code Annotation}注解对象则可以获得{@code value}属性中的{@code Item}注解对象
*
* @return {@link RepeatableAnnotationCollector}实例
* @see Standard
*/
static RepeatableAnnotationCollector standard() {
return Standard.INSTANCE;
}
/**
* 当解析注解属性时将根据给定的判断条件确定该属性中是否含有可重复注解<br>
* 收集器将返回所有匹配的属性中的可重复注解
*
* @param predicate 是否为容纳可重复注解的属性的判断条件
* @return {@link RepeatableAnnotationCollector}实例
*/
static RepeatableAnnotationCollector condition(final BiPredicate<Annotation, Method> predicate) {
return new Condition(predicate);
}
/**
* <p>当注解中存在有属性为注解数组且该数组对应的注解类型被{@link Repeatable}注解时
* 认为该属性包含可重复注解<br>
* 收集器将返回所有符合上述条件的属性中的可重复注解<br>
* eg:
* <pre><code>
* {@literal @}interface Annotation {
* Item1[] items1() default {};
* Item2[] items2() default {};
* }
* </code></pre>
* 解析任意{@code Annotation}注解对象
* 则可以获得{@code items1}属性中的{@code Item1}注解对象
* 以及{@code items2}属性中的{@code Item2}注解对象
*
* @return {@link RepeatableAnnotationCollector}实例
*/
static RepeatableAnnotationCollector full() {
return Full.INSTANCE;
}
/**
* 清空单例缓存
*/
static void clearSingletonCaches() {
Standard.INSTANCE.repeatableMethodCache.clear();
Full.INSTANCE.repeatableMethodCache.clear();
}
/**
* <p>若一个注解是可重复注解的容器注解则尝试通过其属性获得获得包含的注解对象
* 若包含的注解对象也是可重复注解的容器注解则继续解析直到获得所有非容器注解为止<br>
* eg:
* 若存在嵌套关系{@code a -> b -> c}
* 则解析注解<em>a</em>则将得到全部<em>c</em>注解<br>
* 如果注解不包含可重复注解则返回<em>a</em>本身
*
* @param annotation 容器注解
* @return 容器注解中的可重复注解{@code annotation}不为容器注解则数组中仅有其本身一个对象
*/
List<Annotation> getFinalRepeatableAnnotations(final Annotation annotation);
/**
* <p>若一个注解是可重复注解的容器注解则尝试通过其属性获得获得包含的注解对象
* 若包含的注解对象也是可重复注解的容器注解则继续解析直到获得所有非容器注解为止<br>
* eg:
* 若存在嵌套关系{@code a -> b -> c}则解析注解<em>a</em>,
* 将获得全部<em>a</em><em>b</em><em>c</em>注解<br>
* 如果注解不包含可重复注解则返回<em>a</em>本身
*
* @param annotation 容器注解
* @return 容器注解中的可重复注解{@code annotation}不为容器注解则数组中仅有其本身一个对象
*/
List<Annotation> getAllRepeatableAnnotations(final Annotation annotation);
/**
* <p>若一个注解是可重复注解的容器注解则尝试通过其属性获得获得包含的指定类型注解对象<br>
* eg:
* 若存在嵌套关系{@code a -> b -> c}
* <ul>
* <li>解析注解<em>a</em>可获得<em>a</em><em>b</em><em>c</em></li>
* <li>解析注解<em>b</em>可获得<em>b</em><em>c</em></li>
* <li>解析注解<em>c</em>只可获得<em>c</em></li>
* </ul>
*
* @param annotation 容器注解
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 容器注解中的可重复注解
*/
<T extends Annotation> List<T> getRepeatableAnnotations(final Annotation annotation, final Class<T> annotationType);
/**
* 空实现
*/
class None implements RepeatableAnnotationCollector {
/**
* 默认实例
*/
private static final None INSTANCE = new None();
/**
* 默认返回空集合
*
* @param annotation 注解
* @return 空集合
*/
@Override
public List<Annotation> getFinalRepeatableAnnotations(final Annotation annotation) {
return Objects.isNull(annotation) ?
Collections.emptyList() : Collections.singletonList(annotation);
}
/**
* 默认返回空集合
*
* @param annotation 注解
* @return 空集合
*/
@Override
public List<Annotation> getAllRepeatableAnnotations(final Annotation annotation) {
return Objects.isNull(annotation) ?
Collections.emptyList() : Collections.singletonList(annotation);
}
/**
* 默认返回空集合
*
* @param annotation 注解
* @return 空集合
*/
@Override
public <T extends Annotation> List<T> getRepeatableAnnotations(final Annotation annotation, final Class<T> annotationType) {
if (Objects.isNull(annotation)) {
return Collections.emptyList();
}
return Objects.equals(annotation.annotationType(), annotationType) ?
Collections.singletonList(annotationType.cast(annotation)) : Collections.emptyList();
}
}
/**
* {@link RepeatableAnnotationCollector}的基本实现
*/
abstract class AbstractCollector implements RepeatableAnnotationCollector {
/**
* <p>若一个注解是可重复注解的容器注解则尝试通过其属性获得获得包含的注解对象
* 若包含的注解对象也是可重复注解的容器注解则继续解析直到获得所有非容器注解为止
*
* @param annotation 容器注解
* @return 容器注解中的可重复注解{@code annotation}不为容器注解则数组中仅有其本身一个对象
*/
@Override
public final List<Annotation> getFinalRepeatableAnnotations(final Annotation annotation) {
return find(annotation, null, false);
}
/**
* <p>若一个注解是可重复注解的容器注解则尝试通过其属性获得获得包含的注解对象
* 若包含的注解对象也是可重复注解的容器注解则继续解析直到获得所有非容器注解为止<br>
* {@code accumulate}{@code true}返回结果为全量的注解
* eg:
* 若存在嵌套关系{@code a -> b -> c}则解析注解<em>a</em>,
* 将获得全部<em>a</em><em>b</em><em>c</em>注解
* 如果注解不包含可重复注解则返回其本身
*
* @param annotation 容器注解
* @return 容器注解中的可重复注解{@code annotation}不为容器注解则数组中仅有其本身一个对象
*/
@Override
public List<Annotation> getAllRepeatableAnnotations(Annotation annotation) {
return find(annotation, null, true);
}
/**
* 若一个注解是可重复注解的容器注解则尝试通过其属性获得获得包含的指定类型注解对象
*
* @param annotation 容器注解
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 容器注解中的可重复注解
*/
@Override
public <T extends Annotation> List<T> getRepeatableAnnotations(
final Annotation annotation, final Class<T> annotationType) {
final List<Annotation> annotations = find(annotation, t -> Objects.equals(t.annotationType(), annotationType), false);
return annotations.stream()
.map(annotationType::cast)
.collect(Collectors.toList());
}
/**
* 递归遍历注解将其平铺
*/
private List<Annotation> find(
final Annotation annotation, final java.util.function.Predicate<Annotation> condition, final boolean accumulate) {
if (Objects.isNull(annotation)) {
return Collections.emptyList();
}
final boolean hasCondition = Objects.nonNull(condition);
final List<Annotation> results = new ArrayList<>();
final Deque<Annotation> deque = new LinkedList<>();
deque.add(annotation);
while (!deque.isEmpty()) {
final Annotation source = deque.removeFirst();
final List<Method> repeatableMethods = resolveRepeatableMethod(source);
// 若是累加的则记录每一个注解
if (accumulate) {
results.add(source);
}
final boolean isTarget = hasCondition && condition.test(source);
if (CollUtil.isEmpty(repeatableMethods) || isTarget) {
// 不是累加的则仅当正在处理的注解不为可重复注解时才记录
boolean shouldProcess = !accumulate && (!hasCondition || isTarget);
if (shouldProcess) {
results.add(source);
}
continue;
}
final Annotation[] repeatableAnnotation = repeatableMethods.stream()
.map(method -> getRepeatableAnnotationsFormAttribute(source, method))
.filter(ArrayUtil::isNotEmpty)
.flatMap(Stream::of)
.toArray(Annotation[]::new);
if (ArrayUtil.isNotEmpty(repeatableAnnotation)) {
CollUtil.addAll(deque, repeatableAnnotation);
}
}
return results;
}
/**
* 调用{@code value}方法获得嵌套的可重复注解
*
* @param annotation 注解对象
* @param method 容纳可重复注解的方法
* @return 可重复注解
* @throws ClassCastException {@code method}调用结果无法正确转为{@link Annotation[]}类型时抛出
*/
protected Annotation[] getRepeatableAnnotationsFormAttribute(final Annotation annotation, final Method method) {
return MethodUtil.invoke(annotation, method);
}
/**
* 解析获得注解中存放可重复注解的属性
*
* @param annotation 注解
* @return 属性
*/
protected abstract List<Method> resolveRepeatableMethod(final Annotation annotation);
}
/**
* 标准实现当注解中有且仅有一个名为{@code value}的属性时
* 若该属性类型为注解数组且该数组对应的注解类型被{@link Repeatable}注解
* 则收集器将返回该属性中包括的可重复注解
*/
class Standard extends AbstractCollector {
/**
* 默认的value属性
*/
private static final String VALUE = "value";
/**
* 默认实例
*/
private static final Standard INSTANCE = new Standard();
/**
* 空方法缓存
*/
private static final Object NONE = new Object();
/**
* 可重复注解对应的方法缓存
*/
private final Map<Class<? extends Annotation>, Object> repeatableMethodCache = new WeakConcurrentMap<>();
/**
* 构造
*/
Standard() {
}
/**
* 解析获得注解中存放可重复注解的属性
*
* @param annotation 注解
* @return 属性
*/
@Override
protected List<Method> resolveRepeatableMethod(final Annotation annotation) {
final Object cache = MapUtil.computeIfAbsent(
repeatableMethodCache, annotation.annotationType(), this::resolveRepeatableMethodFromType
);
return (cache == NONE) ? null : Collections.singletonList((Method)cache);
}
/**
* 从缓存中获得存放可重复注解的属性
*/
private Object resolveRepeatableMethodFromType(final Class<? extends Annotation> annotationType) {
final Method[] attributes = AnnotationUtil.getAnnotationAttributes(annotationType);
if (attributes.length != 1) {
return NONE;
}
return isRepeatableMethod(attributes[0]) ? attributes[0] : NONE;
}
/**
* 判断方法是否为容器注解的{@code value}方法
*
* @param attribute 注解的属性
* @return 该属性是否为注解存放可重复注解的方法
*/
protected boolean isRepeatableMethod(final Method attribute) {
// 属性名需为value
if (!CharSequenceUtil.equals(VALUE, attribute.getName())) {
return false;
}
final Class<?> attributeType = attribute.getReturnType();
// 返回值类型需为数组
return attributeType.isArray()
// 且数组元素需为注解
&& attributeType.getComponentType()
.isAnnotation()
// 该注解类必须被@Repeatable注解但不要求与当前属性的声明方法一致
&& attributeType.getComponentType()
.isAnnotationPresent(Repeatable.class);
}
}
/**
* 自定义判断条件的实现当解析注解属性时将根据给定的判断条件
* 确定该属性中是否含有可重复注解收集器将返回所有匹配的属性中的可重复注解
*/
class Condition extends AbstractCollector {
/**
* 是否为容纳可重复注解的属性的判断条件
*/
private final BiPredicate<Annotation, Method> predicate;
/**
* 构造
*
* @param predicate 是否为容纳可重复注解的属性的判断条件
*/
Condition(final BiPredicate<Annotation, Method> predicate) {
this.predicate = Objects.requireNonNull(predicate);
}
/**
* 解析获得注解中存放可重复注解的属性
*
* @param annotation 注解
* @return 属性
*/
@Override
protected List<Method> resolveRepeatableMethod(final Annotation annotation) {
return Stream.of(AnnotationUtil.getAnnotationAttributes(annotation.annotationType()))
.filter(method -> predicate.test(annotation, method))
.collect(Collectors.toList());
}
}
/**
* 全量实现当注解中存在有属性为注解数组且该数组对应的注解类型被{@link Repeatable}注解时
* 认为该属性包含可重复注解<br>
* 收集器将返回所有符合上述条件的属性中的可重复注解
*/
class Full extends AbstractCollector {
/**
* 默认实例
*/
private static final Full INSTANCE = new Full();
/**
* 空方法缓存
*/
private static final Object NONE = new Object();
/**
* 可重复注解对应的方法缓存
*/
private final Map<Class<? extends Annotation>, Object> repeatableMethodCache = new WeakConcurrentMap<>();
/**
* 构造
*/
Full() {
}
/**
* 解析获得注解中存放可重复注解的属性
*
* @param annotation 注解
* @return 属性
*/
@SuppressWarnings("unchecked")
@Override
protected List<Method> resolveRepeatableMethod(final Annotation annotation) {
final Object cache = MapUtil.computeIfAbsent(
repeatableMethodCache, annotation.annotationType(), this::resolveRepeatableMethodFromType
);
return (cache == NONE) ? null : (List<Method>)cache;
}
/**
* 从缓存中获得存放可重复注解的属性
*/
private Object resolveRepeatableMethodFromType(final Class<? extends Annotation> annotationType) {
final List<Method> methods = Stream.of(AnnotationUtil.getAnnotationAttributes(annotationType))
.filter(this::isRepeatableMethod)
.collect(Collectors.toList());
return methods.isEmpty() ? NONE : methods;
}
/**
* 判断方法是否为容器注解的{@code value}方法
*
* @param attribute 注解的属性
* @return 该属性是否为注解存放可重复注解的方法
*/
protected boolean isRepeatableMethod(final Method attribute) {
final Class<?> attributeType = attribute.getReturnType();
// 返回值类型需为数组
return attributeType.isArray()
// 且数组元素需为注解
&& attributeType.getComponentType()
.isAnnotation()
// 该注解类必须被@Repeatable注解但不要求与当前属性的声明方法一致
&& attributeType.getComponentType()
.isAnnotationPresent(Repeatable.class);
}
}
}

View File

@ -0,0 +1,389 @@
package cn.hutool.core.annotation;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ArrayUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.*;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
/**
* <p>支持可重复注解的增强{@link AnnotatedElement}
* 功能与{@link MetaAnnotatedElement}类似但是存在下述差异
* <ul>
* <li>
* 限制以同一根注解延伸出的树结构上而不是{@link AnnotatedElement}每种类型注解只能保留一个
* 即当{@link AnnotatedElement}存在多个根注解有相同的元注解时这些元注解会都会被扫描到
* </li>
* <li>
* 支持扫描{@link AnnotatedElement}可重复注解即当当前实例指定的{@link RepeatableAnnotationCollector}
* 支持从{@link AnnotatedElement}上直接声明的注解中获得可重复注解时
* 则将会自动将其展开直到不为容器注解为止<br>
* eg<br>
* A上存在注解<em>X</em>该注解是一个容器注解内部可重复注解<em>Y</em>
* 包含解析后得到注解<em>X</em>与可重复注解<em>Y</em>,<br>
* 同理若存在<em>X</em><em>Y</em><em>X</em>的嵌套关系则解析后获得全部三者
* </li>
* </ul>
* 由于上述机制当通过实例的{@link #getAnnotation(Class)}{@link #getDeclaredAnnotation(Class)}
* 方法获得指定类型注解时若该类型注解存在多个仅能尽可能获得最先被扫描到的那一个
*
* @author huangchengxing
* @since 6.0.0
* @see RepeatableAnnotationCollector
*/
public class RepeatableMetaAnnotatedElement<T extends AnnotationMapping<Annotation>> implements AnnotatedElement, Iterable<T> {
/**
* 包装的{@link AnnotatedElement}对象
*/
private final AnnotatedElement element;
/**
* 创建{@link AnnotationMapping}的工厂方法
*/
private final BiFunction<T, Annotation, T> mappingFactory;
/**
* 解析得到的根注解与元注解的聚合体
*/
private final List<Aggregation> aggregations;
/**
* 可重复注解收集器
*/
private final RepeatableAnnotationCollector repeatableCollector;
/**
* 获取{@link AnnotatedElement}上的注解结构该方法会针对相同的{@link AnnotatedElement}缓存映射对象
*
* @param element 被注解元素
* @param mappingFactory 创建{@link AnnotationMapping}的工厂方法返回值为{@code null}时将忽略该注解
* @param <A> {@link AnnotationMapping}类型
* @return {@link AnnotatedElement}上的注解结构
*/
public static <A extends AnnotationMapping<Annotation>> RepeatableMetaAnnotatedElement<A> create(
final AnnotatedElement element, final BiFunction<A, Annotation, A> mappingFactory) {
return create(RepeatableAnnotationCollector.standard(), element, mappingFactory);
}
/**
* 获取{@link AnnotatedElement}上的注解结构该方法会针对相同的{@link AnnotatedElement}缓存映射对象
*
* @param collector 可重复注解收集器
* @param element 被注解元素
* @param mappingFactory 创建{@link AnnotationMapping}的工厂方法返回值为{@code null}时将忽略该注解
* @param <A> {@link AnnotationMapping}类型
* @return {@link AnnotatedElement}上的注解结构
*/
public static <A extends AnnotationMapping<Annotation>> RepeatableMetaAnnotatedElement<A> create(
final RepeatableAnnotationCollector collector,
final AnnotatedElement element,
final BiFunction<A, Annotation, A> mappingFactory) {
return new RepeatableMetaAnnotatedElement<>(collector, element, mappingFactory);
}
/**
* 创建一个支持可重复注解的增强{@link AnnotatedElement}
*
* @param element 包装的{@link AnnotatedElement}对象
* @param mappingFactory 创建{@link AnnotationMapping}的工厂方法
*/
RepeatableMetaAnnotatedElement(
final RepeatableAnnotationCollector repeatableCollector, final AnnotatedElement element, final BiFunction<T, Annotation, T> mappingFactory) {
this.element = Objects.requireNonNull(element);
this.mappingFactory = Objects.requireNonNull(mappingFactory);
this.repeatableCollector = repeatableCollector;
this.aggregations = Collections.unmodifiableList(initAggregations(element));
}
/**
* 指定注解是否在{@link #element}上直接声明的注解直接声明的注解包含的可重复注解
* 以及他们的元注解中存在
*
* @param annotationType 注解类型
* @return 是否
*/
@Override
public boolean isAnnotationPresent(final Class<? extends Annotation> annotationType) {
return aggregations.stream()
.anyMatch(aggregation -> aggregation.getMappings().containsKey(annotationType));
}
/**
* {@link #element}上直接声明的注解直接声明的注解包含的可重复注解以及它们的元注解中获得指定类型的注解
*
* @param annotationType 注解类型
* @param <A> 注解类型
* @return 注解
*/
@Override
public <A extends Annotation> A getAnnotation(final Class<A> annotationType) {
return aggregations.stream()
.map(Aggregation::getMappings)
.map(annotations -> annotations.get(annotationType))
.filter(Objects::nonNull)
.findFirst()
.map(T::getResolvedAnnotation)
.map(annotationType::cast)
.orElse(null);
}
/**
* 获取{@link #element}上直接声明的注解直接声明的注解包含的可重复注解以及它们的元注解
*
* @return 注解
*/
@Override
public Annotation[] getAnnotations() {
return aggregations.stream()
.map(aggregation -> aggregation.getMappings().values())
.flatMap(Collection::stream)
.map(T::getResolvedAnnotation)
.toArray(Annotation[]::new);
}
/**
* {@link #element}上直接声明的注解直接声明的注解包含的可重复注解以及它们的元注解中获得指定类型的注解
*
* @param annotationType 注解类型
* @param <A> 注解类型
* @return 注解
*/
@Override
public <A extends Annotation> A[] getAnnotationsByType(final Class<A> annotationType) {
return aggregations.stream()
.map(aggregation -> aggregation.getMappings().get(annotationType))
.filter(Objects::nonNull)
.map(T::getResolvedAnnotation)
.map(annotationType::cast)
.toArray(size -> ArrayUtil.newArray(annotationType, size));
}
/**
* 获取由{@link #element}直接声明的注解不包含被直接声明的容器注解包括的可重复注解
*
* @return 注解
*/
@Override
public Annotation[] getDeclaredAnnotations() {
return aggregations.stream()
.filter(Aggregation::isDirect)
.map(Aggregation::getRoot)
.map(T::getResolvedAnnotation)
.toArray(Annotation[]::new);
}
/**
* 获取由{@link #element}直接声明的注解不包含被直接声明的容器注解包括的可重复注解
*
* @param annotationType 注解类型
* @param <A> 注解类型
* @return 注解
*/
@Override
public <A extends Annotation> A getDeclaredAnnotation(final Class<A> annotationType) {
return aggregations.stream()
.filter(Aggregation::isDirect)
.map(Aggregation::getRoot)
.filter(annotation -> Objects.equals(annotationType, annotation.annotationType()))
.findFirst()
.map(T::getResolvedAnnotation)
.map(annotationType::cast)
.orElse(null);
}
/**
* 获取由{@link #element}直接声明的注解不包含被直接声明的容器注解包括的可重复注解
*
* @param annotationType 注解类型
* @param <A> 注解类型
* @return 注解
*/
@Override
public <A extends Annotation> A[] getDeclaredAnnotationsByType(final Class<A> annotationType) {
return aggregations.stream()
.filter(Aggregation::isDirect)
.map(Aggregation::getRoot)
.filter(annotation -> Objects.equals(annotationType, annotation.annotationType()))
.map(T::getResolvedAnnotation)
.map(annotationType::cast)
.toArray(size -> ArrayUtil.newArray(annotationType, size));
}
/**
* 注解对象
*
* @return 被包装的原始元素
*/
public AnnotatedElement getElement() {
return element;
}
/**
* 比较两个实例是否相等
*
* @param o 对象
* @return 是否
*/
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final RepeatableMetaAnnotatedElement<?> that = (RepeatableMetaAnnotatedElement<?>)o;
return element.equals(that.element) && mappingFactory.equals(that.mappingFactory) && repeatableCollector.equals(that.repeatableCollector);
}
/**
* 获取实例的哈希值
*
* @return 哈希值
*/
@Override
public int hashCode() {
return Objects.hash(element, mappingFactory, repeatableCollector);
}
/**
* 获取迭代器
*
* @return 迭代器
*/
@Override
public Iterator<T> iterator() {
return aggregations.stream()
.map(Aggregation::getMappings)
.map(Map::values)
.flatMap(Collection::stream)
.iterator();
}
/**
* 初始化
*/
private List<Aggregation> initAggregations(final AnnotatedElement element) {
// TODO 若有可能一并支持处理元注解中的可重复注解
List<Aggregation> result = new ArrayList<>();
for (final Annotation declaredAnnotation : AnnotationUtil.getDeclaredAnnotations(element)) {
List<Aggregation> repeatableAnnotations = collectRepeatable(declaredAnnotation);
if (CollUtil.isNotEmpty(repeatableAnnotations)) {
result.addAll(repeatableAnnotations);
}
}
return result;
}
/**
* 若当前注解是可重复注解的容器则将其平铺后把可重复注解加入{@link #aggregations}
*/
private List<Aggregation> collectRepeatable(final Annotation annotation) {
return repeatableCollector.getAllRepeatableAnnotations(annotation)
.stream()
.map(a -> new Aggregation(a, Objects.equals(a, annotation)))
.collect(Collectors.toList());
}
/**
* 由根注解与其元注解构成的注解聚合
*/
class Aggregation {
/**
* 根注解
*/
private final T root;
/**
* 注解
*/
private volatile Map<Class<? extends Annotation>, T> mappings;
/**
* 是否是由{@link #element}直接声明的注解
*/
private final boolean isDirect;
/**
* 创建一个合并聚合
*/
public Aggregation(final Annotation root, final boolean isDirect) {
this.root = mappingFactory.apply(null, root);
this.isDirect = isDirect;
}
/**
* 获得收集到的注解
*/
private Map<Class<? extends Annotation>, T> getMappings() {
if (Objects.isNull(mappings)) {
synchronized (this) {
if (Objects.isNull(mappings)) {
mappings = Collections.unmodifiableMap(initMetaAnnotations());
}
}
}
return mappings;
}
/**
* 获得注解及其元注解
*/
private Map<Class<? extends Annotation>, T> initMetaAnnotations() {
final Map<Class<? extends Annotation>, T> collectedMappings = new LinkedHashMap<>();
final Deque<T> deque = new LinkedList<>();
deque.add(root);
while (!deque.isEmpty()) {
final T source = deque.removeFirst();
if (!isNeedMapping(collectedMappings, source)) {
continue;
}
collectedMappings.put(source.annotationType(), source);
for (final Annotation annotation : AnnotationUtil.getDeclaredAnnotations(source.annotationType())) {
if (collectedMappings.containsKey(annotation.annotationType())) {
continue;
}
final T mapping = mappingFactory.apply(source, annotation);
if (Objects.nonNull(mapping) && isNeedMapping(collectedMappings, mapping)) {
deque.addLast(mapping);
}
}
}
return collectedMappings;
}
/**
* 该注解是否需要映射 <br>
* 默认情况下已经处理过或在{@link java.lang}包下的注解不会被处理
*/
private boolean isNeedMapping(final Map<Class<? extends Annotation>, T> mappings, final Annotation annotation) {
return !CharSequenceUtil.startWith(annotation.annotationType().getName(), "java.lang.")
&& !mappings.containsKey(annotation.annotationType());
}
/**
* 根注解是否由{@link #element}直接声明
*
* @return 是否
*/
public boolean isDirect() {
return isDirect;
}
/**
* 获取根注解
*
* @return 根注解
*/
public T getRoot() {
return root;
}
}
}

View File

@ -2,8 +2,7 @@ package cn.hutool.core.annotation;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.multi.MultiValueMap;
import cn.hutool.core.map.multi.SetValueMap;
import cn.hutool.core.map.multi.Graph;
import cn.hutool.core.reflect.ClassUtil;
import cn.hutool.core.reflect.MethodUtil;
import cn.hutool.core.text.CharSequenceUtil;
@ -170,7 +169,7 @@ public class ResolvedAnnotationMapping implements AnnotationMapping<Annotation>
Arrays.fill(this.resolvedAttributes, NOT_FOUND_INDEX);
// 若有必要解析属性
// TODO 可能的改进flag改为枚举使得可以自行选择1.只支持属性别名2.只支持属性覆盖3.两个都支持4.两个都不支持
// TODO flag改为枚举使得可以自行选择1.只支持属性别名2.只支持属性覆盖3.两个都支持4.两个都不支持
this.resolved = resolveAttribute && resolveAttributes();
}
@ -178,6 +177,7 @@ public class ResolvedAnnotationMapping implements AnnotationMapping<Annotation>
* 解析属性
*/
private boolean resolveAttributes() {
// TODO 支持处理@PropIgnore被标记的属性无法被覆写也不会被别名关联
// 解析同一注解中的别名
resolveAliasAttributes();
// 使用子注解覆写当前注解中的属性
@ -478,8 +478,8 @@ public class ResolvedAnnotationMapping implements AnnotationMapping<Annotation>
private void resolveAliasAttributes() {
final Map<Method, Integer> attributeIndexes = new HashMap<>(attributes.length);
final Graph<Method> methodGraph = new Graph<>();
// 解析被作为别名的关联属性根据节点关系构建邻接表
final MultiValueMap<Method, Method> aliasedMethods = new SetValueMap<>();
for (int i = 0; i < attributes.length; i++) {
// 获取属性上的@Alias注解
final Method attribute = attributes[i];
@ -491,15 +491,14 @@ public class ResolvedAnnotationMapping implements AnnotationMapping<Annotation>
// 获取别名属性
final Method aliasAttribute = getAliasAttribute(attribute, attributeAnnotation);
Objects.requireNonNull(aliasAttribute);
aliasedMethods.putValue(aliasAttribute, attribute);
aliasedMethods.putValue(attribute, aliasAttribute);
methodGraph.putEdge(aliasAttribute, attribute);
}
// 按广度优先遍历邻接表将属于同一张图上的节点分为一组并为其建立AliasSet
final Set<Method> accessed = new HashSet<>(attributes.length);
final Set<Method> group = new LinkedHashSet<>();
final Deque<Method> deque = new LinkedList<>();
for (final Method target : aliasedMethods.keySet()) {
for (final Method target : methodGraph.keySet()) {
group.clear();
deque.addLast(target);
while (!deque.isEmpty()) {
@ -511,7 +510,7 @@ public class ResolvedAnnotationMapping implements AnnotationMapping<Annotation>
accessed.add(curr);
// 将其添加到关系组
group.add(curr);
Collection<Method> aliases = aliasedMethods.get(curr);
final Collection<Method> aliases = methodGraph.getAdjacentPoints(curr);
if (CollUtil.isNotEmpty(aliases)) {
deque.addAll(aliases);
}
@ -525,8 +524,8 @@ public class ResolvedAnnotationMapping implements AnnotationMapping<Annotation>
// 根据AliasSet更新关联的属性
Stream.of(aliasSets).filter(Objects::nonNull).forEach(set -> {
final int resolvedIndex = set.resolve();
set.forEach(index -> resolvedAttributes[index] = resolvedIndex);
final int effectiveAttributeIndex = set.determineEffectiveAttribute();
set.forEach(index -> resolvedAttributes[index] = effectiveAttributeIndex);
});
}
@ -616,7 +615,7 @@ public class ResolvedAnnotationMapping implements AnnotationMapping<Annotation>
* <li>若有多个属性具有非默认值则要求所有的非默认值都必须相等若符合并返回该首个具有非默认值的属性否则报错</li>
* </ul>
*/
private int resolve() {
private int determineEffectiveAttribute() {
int resolvedIndex = NOT_FOUND_INDEX;
boolean hasNotDef = false;
Object lastValue = null;

View File

@ -8,6 +8,7 @@ import cn.hutool.core.map.SafeConcurrentHashMap;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.text.CharPool;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.CharUtil;
import java.io.File;
import java.lang.reflect.Array;
@ -40,7 +41,7 @@ public class ClassLoaderUtil {
/**
* 包名分界符: '.'
*/
private static final char PACKAGE_SEPARATOR = StrUtil.C_DOT;
private static final char PACKAGE_SEPARATOR = CharUtil.DOT;
/**
* 内部类分界符: '$'
*/

View File

@ -2,7 +2,7 @@ package cn.hutool.core.classloader;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.net.URLUtil;
import cn.hutool.core.net.url.URLUtil;
import cn.hutool.core.reflect.MethodUtil;
import java.io.File;

View File

@ -13,7 +13,13 @@ import cn.hutool.core.text.StrUtil;
*/
public class Base16Codec implements Encoder<byte[], char[]>, Decoder<CharSequence, byte[]> {
/**
* 编码解码器小写
*/
public static final Base16Codec CODEC_LOWER = new Base16Codec(true);
/**
* 编码解码器大写
*/
public static final Base16Codec CODEC_UPPER = new Base16Codec(false);
private final char[] alphabets;

View File

@ -1,4 +1,4 @@
package cn.hutool.core.lang.hash;
package cn.hutool.core.codec;
/**
* 128位数字表示分高位和低位

View File

@ -166,7 +166,9 @@ public class PercentCodec implements Encoder<byte[], byte[]>, Serializable {
* @author looly
* @since 6.0.0
*/
public static class Builder implements cn.hutool.core.builder.Builder<PercentCodec> {
public static class Builder implements cn.hutool.core.lang.builder.Builder<PercentCodec> {
private static final long serialVersionUID = 1L;
/**
* 从已知PercentCodec创建PercentCodec会复制给定PercentCodec的安全字符
*

View File

@ -85,6 +85,10 @@ public class PunyCode {
}
// Append delimiter
if (b > 0) {
if(b == length){
// 无需要编码的字符
return output.toString();
}
output.append(DELIMITER);
}
int h = b;
@ -158,7 +162,7 @@ public class PunyCode {
if (result.length() != 0) {
result.append(CharUtil.DOT);
}
result.append(decode(str));
result.append(StrUtil.startWithIgnoreEquals(str, PUNY_CODE_PREFIX) ? decode(str) : str);
}
return result.toString();

View File

@ -1,5 +1,6 @@
package cn.hutool.core.lang.hash;
package cn.hutool.core.codec.hash;
import cn.hutool.core.codec.Number128;
import cn.hutool.core.util.ByteUtil;
import java.util.Arrays;
@ -15,18 +16,22 @@ import java.util.Arrays;
* @author hexiufeng
* @since 5.2.5
*/
public class CityHash {
public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>{
public static CityHash INSTANCE = new CityHash();
// Some primes between 2^63 and 2^64 for various uses.
private static final long k0 = 0xc3a5c85c97cb3127L;
private static final long k1 = 0xb492b66fbe98f273L;
private static final long k2 = 0x9ae16a3b2f90404fL;
private static final long kMul = 0x9ddfea08eb382d69L;
// Magic numbers for 32-bit hashing. Copied from Murmur3.
private static final int c1 = 0xcc9e2d51;
private static final int c2 = 0x1b873593;
@Override
public Number encode(final byte[] bytes) {
return hash64(bytes);
}
/**
* 计算32位City Hash值
@ -34,7 +39,8 @@ public class CityHash {
* @param data 数据
* @return hash值
*/
public static int hash32(final byte[] data) {
@Override
public int hash32(final byte[] data) {
final int len = data.length;
if (len <= 24) {
return len <= 12 ?
@ -117,7 +123,8 @@ public class CityHash {
* @param data 数据
* @return hash值
*/
public static long hash64(final byte[] data) {
@Override
public long hash64(final byte[] data) {
int len = data.length;
if (len <= 32) {
if (len <= 16) {
@ -168,7 +175,7 @@ public class CityHash {
* @param seed1 种子2
* @return hash值
*/
public static long hash64(final byte[] data, final long seed0, final long seed1) {
public long hash64(final byte[] data, final long seed0, final long seed1) {
return hashLen16(hash64(data) - seed0, seed1);
}
@ -179,7 +186,7 @@ public class CityHash {
* @param seed 种子2
* @return hash值
*/
public static long hash64(final byte[] data, final long seed) {
public long hash64(final byte[] data, final long seed) {
return hash64(data, k2, seed);
}
@ -189,7 +196,8 @@ public class CityHash {
* @param data 数据
* @return hash值
*/
public static Number128 hash128(final byte[] data) {
@Override
public Number128 hash128(final byte[] data) {
final int len = data.length;
return len >= 16 ?
hash128(data, 16,
@ -204,12 +212,12 @@ public class CityHash {
* @param seed 种子
* @return hash值
*/
public static Number128 hash128(final byte[] data, final Number128 seed) {
public Number128 hash128(final byte[] data, final Number128 seed) {
return hash128(data, 0, seed);
}
//------------------------------------------------------------------------------------------------------- Private method start
private static Number128 hash128(final byte[] byteArray, final int start, final Number128 seed) {
private Number128 hash128(final byte[] byteArray, final int start, final Number128 seed) {
int len = byteArray.length - start;
if (len < 128) {
@ -283,7 +291,7 @@ public class CityHash {
}
private static int hash32Len0to4(final byte[] byteArray) {
private int hash32Len0to4(final byte[] byteArray) {
int b = 0;
int c = 9;
final int len = byteArray.length;
@ -294,7 +302,7 @@ public class CityHash {
return fmix(mur(b, mur(len, c)));
}
private static int hash32Len5to12(final byte[] byteArray) {
private int hash32Len5to12(final byte[] byteArray) {
final int len = byteArray.length;
int a = len;
int b = len * 5;
@ -306,7 +314,7 @@ public class CityHash {
return fmix(mur(c, mur(b, mur(a, d))));
}
private static int hash32Len13to24(final byte[] byteArray) {
private int hash32Len13to24(final byte[] byteArray) {
final int len = byteArray.length;
final int a = fetch32(byteArray, (len >>> 1) - 4);
final int b = fetch32(byteArray, 4);
@ -319,7 +327,7 @@ public class CityHash {
return fmix(mur(f, mur(e, mur(d, mur(c, mur(b, mur(a, h)))))));
}
private static long hashLen0to16(final byte[] byteArray) {
private long hashLen0to16(final byte[] byteArray) {
final int len = byteArray.length;
if (len >= 8) {
final long mul = k2 + len * 2L;
@ -346,7 +354,7 @@ public class CityHash {
}
// This probably works well for 16-byte strings as well, but it may be overkill in that case.
private static long hashLen17to32(final byte[] byteArray) {
private long hashLen17to32(final byte[] byteArray) {
final int len = byteArray.length;
final long mul = k2 + len * 2L;
final long a = fetch64(byteArray, 0) * k1;
@ -357,7 +365,7 @@ public class CityHash {
a + rotate64(b + k2, 18) + c, mul);
}
private static long hashLen33to64(final byte[] byteArray) {
private long hashLen33to64(final byte[] byteArray) {
final int len = byteArray.length;
final long mul = k2 + len * 2L;
long a = fetch64(byteArray, 0) * k2;
@ -407,12 +415,13 @@ public class CityHash {
return b;
}
private static long hashLen16(final long u, final long v) {
private long hashLen16(final long u, final long v) {
return hash128to64(new Number128(u, v));
}
private static long hash128to64(final Number128 number128) {
private long hash128to64(final Number128 number128) {
// Murmur-inspired hashing.
final long kMul = 0x9ddfea08eb382d69L;
long a = (number128.getLowValue() ^ number128.getHighValue()) * kMul;
a ^= (a >>> 47);
long b = (number128.getHighValue() ^ a) * kMul;
@ -434,7 +443,7 @@ public class CityHash {
return h;
}
private static int mur(int a, int h) {
private int mur(int a, int h) {
// Helper from Murmur3 for combining two 32-bit values.
a *= c1;
a = rotate32(a, 17);
@ -466,7 +475,7 @@ public class CityHash {
b);
}
private static Number128 cityMurmur(final byte[] byteArray, final Number128 seed) {
private Number128 cityMurmur(final byte[] byteArray, final Number128 seed) {
final int len = byteArray.length;
long a = seed.getLowValue();
long b = seed.getHighValue();

View File

@ -1,4 +1,4 @@
package cn.hutool.core.lang.hash;
package cn.hutool.core.codec.hash;
import java.io.Serializable;
import java.util.Collection;

View File

@ -1,4 +1,7 @@
package cn.hutool.core.lang.hash;
package cn.hutool.core.codec.hash;
import cn.hutool.core.codec.Encoder;
import cn.hutool.core.codec.Number128;
/**
* Hash计算接口
@ -8,7 +11,7 @@ package cn.hutool.core.lang.hash;
* @since 5.2.5
*/
@FunctionalInterface
public interface Hash128<T> extends Hash<T>{
public interface Hash128<T> extends Encoder<T, Number> {
/**
* 计算Hash值
@ -19,7 +22,7 @@ public interface Hash128<T> extends Hash<T>{
Number128 hash128(T t);
@Override
default Number hash(final T t){
default Number encode(final T t){
return hash128(t);
}
}

View File

@ -1,4 +1,6 @@
package cn.hutool.core.lang.hash;
package cn.hutool.core.codec.hash;
import cn.hutool.core.codec.Encoder;
/**
* Hash计算接口
@ -8,7 +10,7 @@ package cn.hutool.core.lang.hash;
* @since 5.2.5
*/
@FunctionalInterface
public interface Hash32<T> extends Hash<T>{
public interface Hash32<T> extends Encoder<T, Number> {
/**
* 计算Hash值
*
@ -18,7 +20,7 @@ public interface Hash32<T> extends Hash<T>{
int hash32(T t);
@Override
default Number hash(final T t){
default Number encode(final T t){
return hash32(t);
}
}

View File

@ -1,4 +1,6 @@
package cn.hutool.core.lang.hash;
package cn.hutool.core.codec.hash;
import cn.hutool.core.codec.Encoder;
/**
* Hash计算接口
@ -8,7 +10,7 @@ package cn.hutool.core.lang.hash;
* @since 5.2.5
*/
@FunctionalInterface
public interface Hash64<T> extends Hash<T>{
public interface Hash64<T> extends Encoder<T, Number> {
/**
* 计算Hash值
*
@ -18,7 +20,7 @@ public interface Hash64<T> extends Hash<T>{
long hash64(T t);
@Override
default Number hash(final T t){
default Number encode(final T t){
return hash64(t);
}
}

View File

@ -1,4 +1,6 @@
package cn.hutool.core.lang.hash;
package cn.hutool.core.codec.hash;
import cn.hutool.core.codec.Number128;
/**
* Hash算法大全<br>
@ -449,7 +451,7 @@ public class HashUtil {
* @since 4.3.3
*/
public static int murmur32(final byte[] data) {
return MurmurHash.hash32(data);
return MurmurHash.INSTANCE.hash32(data);
}
/**
@ -460,7 +462,7 @@ public class HashUtil {
* @since 4.3.3
*/
public static long murmur64(final byte[] data) {
return MurmurHash.hash64(data);
return MurmurHash.INSTANCE.hash64(data);
}
/**
@ -470,8 +472,8 @@ public class HashUtil {
* @return hash值
* @since 4.3.3
*/
public static long[] murmur128(final byte[] data) {
return MurmurHash.hash128(data);
public static Number128 murmur128(final byte[] data) {
return MurmurHash.INSTANCE.hash128(data);
}
/**
@ -482,7 +484,7 @@ public class HashUtil {
* @since 5.2.5
*/
public static int cityHash32(final byte[] data) {
return CityHash.hash32(data);
return CityHash.INSTANCE.hash32(data);
}
/**
@ -494,7 +496,7 @@ public class HashUtil {
* @since 5.2.5
*/
public static long cityHash64(final byte[] data, final long seed) {
return CityHash.hash64(data, seed);
return CityHash.INSTANCE.hash64(data, seed);
}
/**
@ -507,7 +509,7 @@ public class HashUtil {
* @since 5.2.5
*/
public static long cityHash64(final byte[] data, final long seed0, final long seed1) {
return CityHash.hash64(data, seed0, seed1);
return CityHash.INSTANCE.hash64(data, seed0, seed1);
}
/**
@ -518,7 +520,7 @@ public class HashUtil {
* @since 5.2.5
*/
public static long cityHash64(final byte[] data) {
return CityHash.hash64(data);
return CityHash.INSTANCE.hash64(data);
}
/**
@ -529,7 +531,7 @@ public class HashUtil {
* @since 5.2.5
*/
public static long[] cityHash128(final byte[] data) {
return CityHash.hash128(data).getLongArray();
return CityHash.INSTANCE.hash128(data).getLongArray();
}
/**
@ -541,7 +543,7 @@ public class HashUtil {
* @since 5.2.5
*/
public static long[] cityHash128(final byte[] data, final Number128 seed) {
return CityHash.hash128(data, seed).getLongArray();
return CityHash.INSTANCE.hash128(data, seed).getLongArray();
}
/**
@ -552,7 +554,7 @@ public class HashUtil {
* @return hash值
*/
public static long metroHash64(final byte[] data, final long seed) {
return MetroHash.hash64(data, seed);
return MetroHash.INSTANCE.hash64(data, seed);
}
/**
@ -562,7 +564,7 @@ public class HashUtil {
* @return hash值
*/
public static long metroHash64(final byte[] data) {
return MetroHash.hash64(data);
return MetroHash.INSTANCE.hash64(data);
}
/**
@ -573,7 +575,7 @@ public class HashUtil {
* @return hash值long[0]低位long[1]高位
*/
public static long[] metroHash128(final byte[] data, final long seed) {
return MetroHash.hash128(data, seed).getLongArray();
return MetroHash.INSTANCE.hash128(data, seed).getLongArray();
}
/**
@ -583,7 +585,7 @@ public class HashUtil {
* @return hash值long[0]低位long[1]高位
*/
public static long[] metroHash128(final byte[] data) {
return MetroHash.hash128(data).getLongArray();
return MetroHash.INSTANCE.hash128(data).getLongArray();
}
/**

View File

@ -1,7 +1,6 @@
package cn.hutool.core.lang.hash;
package cn.hutool.core.codec.hash;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.text.StrUtil;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@ -12,10 +11,10 @@ import java.security.NoSuchAlgorithmException;
* @author looly
* @since 5.7.20
*/
public class KetamaHash implements Hash64<String>, Hash32<String> {
public class KetamaHash implements Hash64<byte[]>, Hash32<byte[]> {
@Override
public long hash64(final String key) {
public long hash64(final byte[] key) {
final byte[] bKey = md5(key);
return ((long) (bKey[3] & 0xFF) << 24)
| ((long) (bKey[2] & 0xFF) << 16)
@ -24,12 +23,12 @@ public class KetamaHash implements Hash64<String>, Hash32<String> {
}
@Override
public int hash32(final String key) {
public int hash32(final byte[] key) {
return (int) (hash64(key) & 0xffffffffL);
}
@Override
public Number hash(final String key) {
public Number encode(final byte[] key) {
return hash64(key);
}
@ -39,13 +38,13 @@ public class KetamaHash implements Hash64<String>, Hash32<String> {
* @param key 被计算的键
* @return MD5值
*/
private static byte[] md5(final String key) {
private static byte[] md5(final byte[] key) {
final MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (final NoSuchAlgorithmException e) {
throw new UtilException("MD5 algorithm not suooport!", e);
}
return md5.digest(StrUtil.utf8Bytes(key));
return md5.digest(key);
}
}

View File

@ -1,5 +1,6 @@
package cn.hutool.core.lang.hash;
package cn.hutool.core.codec.hash;
import cn.hutool.core.codec.Number128;
import cn.hutool.core.util.ByteUtil;
import java.nio.ByteOrder;
@ -13,35 +14,35 @@ import java.util.Arrays;
* 官方实现https://github.com/jandrewrogers/MetroHash
* 官方文档http://www.jandrewrogers.com/2015/05/27/metrohash/
* Go语言实现https://github.com/linvon/cuckoo-filter/blob/main/vendor/github.com/dgryski/go-metro/
*
* @author li
*/
public class MetroHash {
public class MetroHash implements Hash64<byte[]>, Hash128<byte[]> {
public static MetroHash INSTANCE = new MetroHash();
/**
* hash64 种子加盐
*/
private final static long k0_64 = 0xD6D018F5;
private final static long k1_64 = 0xA2AA033B;
private final static long k2_64 = 0x62992FC1;
private final static long k3_64 = 0x30BC5B29;
@Override
public Number encode(final byte[] bytes) {
return hash64(bytes);
}
/**
* hash128 种子加盐
*/
private final static long k0_128 = 0xC83A91E1;
private final static long k1_128 = 0x8648DBDB;
private final static long k2_128 = 0x7BDEC03B;
private final static long k3_128 = 0x2F5870A5;
public static long hash64(final byte[] data) {
@Override
public long hash64(final byte[] data) {
return hash64(data, 1337);
}
public static Number128 hash128(final byte[] data) {
return hash128(data, 1337);
}
/**
* 计算64位Hash值
*
* @param data 数据
* @param seed 种子
* @return hash64
*/
public long hash64(final byte[] data, final long seed) {
final long k0_64 = 0xD6D018F5;
final long k1_64 = 0xA2AA033B;
final long k2_64 = 0x62992FC1;
final long k3_64 = 0x30BC5B29;
public static long hash64(final byte[] data, final long seed) {
byte[] buffer = data;
long hash = (seed + k2_64) * k0_64;
@ -113,7 +114,24 @@ public class MetroHash {
return hash;
}
public static Number128 hash128(final byte[] data, final long seed) {
@Override
public Number128 hash128(final byte[] data) {
return hash128(data, 1337);
}
/**
* 计算128位hash值
*
* @param data 数据
* @param seed 种子
* @return hash128
*/
public Number128 hash128(final byte[] data, final long seed) {
final long k0_128 = 0xC83A91E1;
final long k1_128 = 0x8648DBDB;
final long k2_128 = 0x7BDEC03B;
final long k3_128 = 0x2F5870A5;
byte[] buffer = data;
long v0, v1, v2, v3;
@ -193,6 +211,7 @@ public class MetroHash {
}
// region =========== Private methods
private static long littleEndian64(final byte[] b, final int start) {
return ByteUtil.bytesToLong(b, start, ByteOrder.LITTLE_ENDIAN);
}
@ -214,4 +233,5 @@ public class MetroHash {
private static long rotateRight(final long val, final int shift) {
return (val >> shift) | (val << (64 - shift));
}
// endregion =========== Private methods
}

View File

@ -1,27 +1,27 @@
package cn.hutool.core.lang.hash;
package cn.hutool.core.codec.hash;
import cn.hutool.core.codec.Number128;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ByteUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.text.StrUtil;
import java.io.Serializable;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
/**
* Murmur3 32bit64bit128bit 哈希算法实现<br>
* 此算法来自于https://github.com/xlturing/Simhash4J/blob/master/src/main/java/bee/simhash/main/Murmur3.java
* 此算法来自于<a href="https://github.com/xlturing/Simhash4J/blob/master/src/main/java/bee/simhash/main/Murmur3.java">...</a>
*
* <p>
* 32-bit Java port of https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp#94 <br>
* 128-bit Java port of https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp#255
* </p>
*
* @author looly,Simhash4J
* @author looly, Simhash4J
* @since 4.3.3
*/
public class MurmurHash implements Serializable{
private static final long serialVersionUID = 1L;
public class MurmurHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>{
public static final MurmurHash INSTANCE = new MurmurHash();
// Constants for 32 bit variant
private static final int C1_32 = 0xcc9e2d51;
@ -45,13 +45,18 @@ public class MurmurHash implements Serializable{
private static final Charset DEFAULT_CHARSET = CharsetUtil.UTF_8;
private static final ByteOrder DEFAULT_ORDER = ByteOrder.LITTLE_ENDIAN;
@Override
public Number encode(final byte[] bytes) {
return hash128(bytes);
}
/**
* Murmur3 32-bit Hash值计算
*
* @param data 数据
* @return Hash值
*/
public static int hash32(final CharSequence data) {
public int hash32(final CharSequence data) {
return hash32(StrUtil.bytes(data, DEFAULT_CHARSET));
}
@ -61,62 +66,65 @@ public class MurmurHash implements Serializable{
* @param data 数据
* @return Hash值
*/
public static int hash32(final byte[] data) {
@Override
public int hash32(final byte[] data) {
return hash32(data, data.length, DEFAULT_SEED);
}
/**
* Murmur3 32-bit Hash值计算
*
* @param data 数据
* @param data 数据
* @param length 长度
* @param seed 种子默认0
* @param seed 种子默认0
* @return Hash值
*/
public static int hash32(final byte[] data, final int length, final int seed) {
public int hash32(final byte[] data, final int length, final int seed) {
return hash32(data, 0, length, seed);
}
/**
* Murmur3 32-bit Hash值计算
*
* @param data 数据
* @param offset 数据开始位置
* @param length 长度
* @param seed 种子默认0
* @return Hash值
*/
public int hash32(final byte[] data, final int offset, final int length, final int seed) {
int hash = seed;
final int nblocks = length >> 2;
// body
for (int i = 0; i < nblocks; i++) {
final int i4 = i << 2;
int k = ByteUtil.bytesToInt(data, i4, DEFAULT_ORDER);
final int i4 = offset + (i << 2);
final int k = ByteUtil.bytesToInt(data, i4, DEFAULT_ORDER);
// mix functions
k *= C1_32;
k = Integer.rotateLeft(k, R1_32);
k *= C2_32;
hash ^= k;
hash = Integer.rotateLeft(hash, R2_32) * M_32 + N_32;
hash = mix32(k, hash);
}
// tail
final int idx = nblocks << 2;
final int idx = offset + (nblocks << 2);
int k1 = 0;
switch (length - idx) {
case 3:
k1 ^= data[idx + 2] << 16;
case 2:
k1 ^= data[idx + 1] << 8;
case 1:
k1 ^= data[idx];
switch (offset + length - idx) {
case 3:
k1 ^= (data[idx + 2] & 0xff) << 16;
case 2:
k1 ^= (data[idx + 1] & 0xff) << 8;
case 1:
k1 ^= (data[idx] & 0xff);
// mix functions
k1 *= C1_32;
k1 = Integer.rotateLeft(k1, R1_32);
k1 *= C2_32;
hash ^= k1;
// mix functions
k1 *= C1_32;
k1 = Integer.rotateLeft(k1, R1_32);
k1 *= C2_32;
hash ^= k1;
}
// finalization
hash ^= length;
hash ^= (hash >>> 16);
hash *= 0x85ebca6b;
hash ^= (hash >>> 13);
hash *= 0xc2b2ae35;
hash ^= (hash >>> 16);
return hash;
return fmix32(hash);
}
/**
@ -125,7 +133,7 @@ public class MurmurHash implements Serializable{
* @param data 数据
* @return Hash值
*/
public static long hash64(final CharSequence data) {
public long hash64(final CharSequence data) {
return hash64(StrUtil.bytes(data, DEFAULT_CHARSET));
}
@ -133,24 +141,24 @@ public class MurmurHash implements Serializable{
* Murmur3 64-bit 算法<br>
* This is essentially MSB 8 bytes of Murmur3 128-bit variant.
*
*
* @param data 数据
* @return Hash值
*/
public static long hash64(final byte[] data) {
@Override
public long hash64(final byte[] data) {
return hash64(data, data.length, DEFAULT_SEED);
}
/**
* Murmur3 64-bit 算法 <br>
* Murmur3 64-bit 算法 <br>
* This is essentially MSB 8 bytes of Murmur3 128-bit variant.
*
* @param data 数据
* @param data 数据
* @param length 长度
* @param seed 种子默认0
* @param seed 种子默认0
* @return Hash值
*/
public static long hash64(final byte[] data, final int length, final int seed) {
public long hash64(final byte[] data, final int length, final int seed) {
long hash = seed;
final int nblocks = length >> 3;
@ -171,24 +179,24 @@ public class MurmurHash implements Serializable{
long k1 = 0;
final int tailStart = nblocks << 3;
switch (length - tailStart) {
case 7:
k1 ^= ((long) data[tailStart + 6] & 0xff) << 48;
case 6:
k1 ^= ((long) data[tailStart + 5] & 0xff) << 40;
case 5:
k1 ^= ((long) data[tailStart + 4] & 0xff) << 32;
case 4:
k1 ^= ((long) data[tailStart + 3] & 0xff) << 24;
case 3:
k1 ^= ((long) data[tailStart + 2] & 0xff) << 16;
case 2:
k1 ^= ((long) data[tailStart + 1] & 0xff) << 8;
case 1:
k1 ^= ((long) data[tailStart] & 0xff);
k1 *= C1;
k1 = Long.rotateLeft(k1, R1);
k1 *= C2;
hash ^= k1;
case 7:
k1 ^= ((long) data[tailStart + 6] & 0xff) << 48;
case 6:
k1 ^= ((long) data[tailStart + 5] & 0xff) << 40;
case 5:
k1 ^= ((long) data[tailStart + 4] & 0xff) << 32;
case 4:
k1 ^= ((long) data[tailStart + 3] & 0xff) << 24;
case 3:
k1 ^= ((long) data[tailStart + 2] & 0xff) << 16;
case 2:
k1 ^= ((long) data[tailStart + 1] & 0xff) << 8;
case 1:
k1 ^= ((long) data[tailStart] & 0xff);
k1 *= C1;
k1 = Long.rotateLeft(k1, R1);
k1 *= C2;
hash ^= k1;
}
// finalization
@ -204,7 +212,7 @@ public class MurmurHash implements Serializable{
* @param data 数据
* @return Hash值 (2 longs)
*/
public static long[] hash128(final CharSequence data) {
public Number128 hash128(final CharSequence data) {
return hash128(StrUtil.bytes(data, DEFAULT_CHARSET));
}
@ -214,26 +222,43 @@ public class MurmurHash implements Serializable{
* @param data -数据
* @return Hash值 (2 longs)
*/
public static long[] hash128(final byte[] data) {
@Override
public Number128 hash128(final byte[] data) {
return hash128(data, data.length, DEFAULT_SEED);
}
/**
* Murmur3 128-bit variant.
*
* @param data 数据
* @param data 数据
* @param length 长度
* @param seed 种子默认0
* @param seed 种子默认0
* @return Hash值(2 longs)
*/
public static long[] hash128(final byte[] data, final int length, final int seed) {
public Number128 hash128(final byte[] data, final int length, final int seed) {
return hash128(data, 0, length, seed);
}
/**
* Murmur3 128-bit variant.
*
* @param data 数据
* @param offset 数据开始位置
* @param length 长度
* @param seed 种子默认0
* @return Hash值(2 longs)
*/
public Number128 hash128(final byte[] data, final int offset, final int length, int seed) {
// 避免负数的种子
seed &= 0xffffffffL;
long h1 = seed;
long h2 = seed;
final int nblocks = length >> 4;
// body
for (int i = 0; i < nblocks; i++) {
final int i16 = i << 4;
final int i16 = offset + (i << 4);
long k1 = ByteUtil.bytesToLong(data, i16, DEFAULT_ORDER);
long k2 = ByteUtil.bytesToLong(data, i16 + 8, DEFAULT_ORDER);
@ -259,47 +284,47 @@ public class MurmurHash implements Serializable{
// tail
long k1 = 0;
long k2 = 0;
final int tailStart = nblocks << 4;
switch (length - tailStart) {
case 15:
k2 ^= (long) (data[tailStart + 14] & 0xff) << 48;
case 14:
k2 ^= (long) (data[tailStart + 13] & 0xff) << 40;
case 13:
k2 ^= (long) (data[tailStart + 12] & 0xff) << 32;
case 12:
k2 ^= (long) (data[tailStart + 11] & 0xff) << 24;
case 11:
k2 ^= (long) (data[tailStart + 10] & 0xff) << 16;
case 10:
k2 ^= (long) (data[tailStart + 9] & 0xff) << 8;
case 9:
k2 ^= data[tailStart + 8] & 0xff;
k2 *= C2;
k2 = Long.rotateLeft(k2, R3);
k2 *= C1;
h2 ^= k2;
final int tailStart = offset + (nblocks << 4);
switch (offset + length - tailStart) {
case 15:
k2 ^= (long) (data[tailStart + 14] & 0xff) << 48;
case 14:
k2 ^= (long) (data[tailStart + 13] & 0xff) << 40;
case 13:
k2 ^= (long) (data[tailStart + 12] & 0xff) << 32;
case 12:
k2 ^= (long) (data[tailStart + 11] & 0xff) << 24;
case 11:
k2 ^= (long) (data[tailStart + 10] & 0xff) << 16;
case 10:
k2 ^= (long) (data[tailStart + 9] & 0xff) << 8;
case 9:
k2 ^= data[tailStart + 8] & 0xff;
k2 *= C2;
k2 = Long.rotateLeft(k2, R3);
k2 *= C1;
h2 ^= k2;
case 8:
k1 ^= (long) (data[tailStart + 7] & 0xff) << 56;
case 7:
k1 ^= (long) (data[tailStart + 6] & 0xff) << 48;
case 6:
k1 ^= (long) (data[tailStart + 5] & 0xff) << 40;
case 5:
k1 ^= (long) (data[tailStart + 4] & 0xff) << 32;
case 4:
k1 ^= (long) (data[tailStart + 3] & 0xff) << 24;
case 3:
k1 ^= (long) (data[tailStart + 2] & 0xff) << 16;
case 2:
k1 ^= (long) (data[tailStart + 1] & 0xff) << 8;
case 1:
k1 ^= data[tailStart] & 0xff;
k1 *= C1;
k1 = Long.rotateLeft(k1, R1);
k1 *= C2;
h1 ^= k1;
case 8:
k1 ^= (long) (data[tailStart + 7] & 0xff) << 56;
case 7:
k1 ^= (long) (data[tailStart + 6] & 0xff) << 48;
case 6:
k1 ^= (long) (data[tailStart + 5] & 0xff) << 40;
case 5:
k1 ^= (long) (data[tailStart + 4] & 0xff) << 32;
case 4:
k1 ^= (long) (data[tailStart + 3] & 0xff) << 24;
case 3:
k1 ^= (long) (data[tailStart + 2] & 0xff) << 16;
case 2:
k1 ^= (long) (data[tailStart + 1] & 0xff) << 8;
case 1:
k1 ^= data[tailStart] & 0xff;
k1 *= C1;
k1 = Long.rotateLeft(k1, R1);
k1 *= C2;
h1 ^= k1;
}
// finalization
@ -315,7 +340,24 @@ public class MurmurHash implements Serializable{
h1 += h2;
h2 += h1;
return new long[] { h1, h2 };
return new Number128(h1, h2);
}
private static int mix32(int k, int hash) {
k *= C1_32;
k = Integer.rotateLeft(k, R1_32);
k *= C2_32;
hash ^= k;
return Integer.rotateLeft(hash, R2_32) * M_32 + N_32;
}
private static int fmix32(int hash) {
hash ^= (hash >>> 16);
hash *= 0x85ebca6b;
hash ^= (hash >>> 13);
hash *= 0xc2b2ae35;
hash ^= (hash >>> 16);
return hash;
}
private static long fmix64(long h) {

View File

@ -1,6 +1,4 @@
package cn.hutool.core.text;
import cn.hutool.core.lang.hash.MurmurHash;
package cn.hutool.core.codec.hash;
import java.math.BigInteger;
import java.util.ArrayList;
@ -23,16 +21,22 @@ import java.util.concurrent.locks.StampedLock;
* @author Looly, litaoxiao
* @since 4.3.3
*/
public class Simhash {
public class Simhash implements Hash64<Collection<? extends CharSequence>> {
private final int bitNum = 64;
/** 存储段数默认按照4段进行simhash存储 */
/**
* 存储段数默认按照4段进行simhash存储
*/
private final int fracCount;
private final int fracBitNum;
/** 汉明距离的衡量标准,小于此距离标准表示相似 */
/**
* 汉明距离的衡量标准小于此距离标准表示相似
*/
private final int hammingThresh;
/** 按照分段存储simhash查找更快速 */
/**
* 按照分段存储simhash查找更快速
*/
private final List<Map<String, List<Long>>> storage;
private final StampedLock lock = new StampedLock();
@ -46,7 +50,7 @@ public class Simhash {
/**
* 构造
*
* @param fracCount 存储段数
* @param fracCount 存储段数
* @param hammingThresh 汉明距离的衡量标准
*/
public Simhash(final int fracCount, final int hammingThresh) {
@ -65,13 +69,14 @@ public class Simhash {
* @param segList 分词的词列表
* @return Hash值
*/
public long hash(final Collection<? extends CharSequence> segList) {
@Override
public long hash64(final Collection<? extends CharSequence> segList) {
final int bitNum = this.bitNum;
// 按照词语的hash值计算simHashWeight(低位对齐)
final int[] weight = new int[bitNum];
long wordHash;
for (final CharSequence seg : segList) {
wordHash = MurmurHash.hash64(seg);
wordHash = MurmurHash.INSTANCE.hash64(seg);
for (int i = 0; i < bitNum; i++) {
if (((wordHash >> i) & 1) == 1)
weight[i] += 1;
@ -96,7 +101,7 @@ public class Simhash {
* @return 是否重复
*/
public boolean equals(final Collection<? extends CharSequence> segList) {
final long simhash = hash(segList);
final long simhash = hash64(segList);
final List<String> fracList = splitSimhash(simhash);
final int hammingThresh = this.hammingThresh;
@ -153,6 +158,7 @@ public class Simhash {
}
//------------------------------------------------------------------------------------------------------ Private method start
/**
* 计算汉明距离
*

View File

@ -4,4 +4,4 @@
* @author looly
*
*/
package cn.hutool.core.lang.hash;
package cn.hutool.core.codec.hash;

View File

@ -1,6 +1,7 @@
package cn.hutool.core.collection;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.hash.Hash32;
import cn.hutool.core.collection.iter.IterUtil;
import cn.hutool.core.collection.iter.IteratorEnumeration;
import cn.hutool.core.comparator.CompareUtil;
@ -9,7 +10,8 @@ import cn.hutool.core.comparator.PropertyComparator;
import cn.hutool.core.convert.CompositeConverter;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.hash.Hash32;
import cn.hutool.core.lang.func.SerBiConsumer;
import cn.hutool.core.lang.func.SerConsumer3;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.reflect.ClassUtil;
import cn.hutool.core.reflect.ConstructorUtil;
@ -21,7 +23,6 @@ import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.ObjUtil;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.AbstractCollection;
import java.util.ArrayList;
@ -1942,14 +1943,14 @@ public class CollUtil {
// ------------------------------------------------------------------------------------------------- forEach
/**
* 循环遍历 {@link Iterable}使用{@link IndexedConsumer} 接受遍历的每条数据并针对每条数据做处理
* 循环遍历 {@link Iterable}使用{@link SerBiConsumer} 接受遍历的每条数据并针对每条数据做处理
*
* @param <T> 集合元素类型
* @param iterable {@link Iterable}
* @param consumer {@link IndexedConsumer} 遍历的每条数据处理器
* @param consumer {@link SerBiConsumer} 遍历的每条数据处理器
* @since 5.4.7
*/
public static <T> void forEach(final Iterable<T> iterable, final IndexedConsumer<T> consumer) {
public static <T> void forEach(final Iterable<T> iterable, final SerBiConsumer<T, Integer> consumer) {
if (iterable == null) {
return;
}
@ -1957,13 +1958,13 @@ public class CollUtil {
}
/**
* 循环遍历 {@link Iterator}使用{@link IndexedConsumer} 接受遍历的每条数据并针对每条数据做处理
* 循环遍历 {@link Iterator}使用{@link SerBiConsumer} 接受遍历的每条数据并针对每条数据做处理
*
* @param <T> 集合元素类型
* @param iterator {@link Iterator}
* @param consumer {@link IndexedConsumer} 遍历的每条数据处理器
* @param consumer {@link SerBiConsumer} 遍历的每条数据处理器
*/
public static <T> void forEach(final Iterator<T> iterator, final IndexedConsumer<T> consumer) {
public static <T> void forEach(final Iterator<T> iterator, final SerBiConsumer<T, Integer> consumer) {
if (iterator == null) {
return;
}
@ -1975,13 +1976,13 @@ public class CollUtil {
}
/**
* 循环遍历 {@link Enumeration}使用{@link IndexedConsumer} 接受遍历的每条数据并针对每条数据做处理
* 循环遍历 {@link Enumeration}使用{@link SerBiConsumer} 接受遍历的每条数据并针对每条数据做处理
*
* @param <T> 集合元素类型
* @param enumeration {@link Enumeration}
* @param consumer {@link IndexedConsumer} 遍历的每条数据处理器
* @param consumer {@link SerBiConsumer} 遍历的每条数据处理器
*/
public static <T> void forEach(final Enumeration<T> enumeration, final IndexedConsumer<T> consumer) {
public static <T> void forEach(final Enumeration<T> enumeration, final SerBiConsumer<T, Integer> consumer) {
if (enumeration == null) {
return;
}
@ -1993,15 +1994,15 @@ public class CollUtil {
}
/**
* 循环遍历Map使用{@link IndexedKVConsumer} 接受遍历的每条数据并针对每条数据做处理<br>
* 循环遍历Map使用{@link SerConsumer3} 接受遍历的每条数据并针对每条数据做处理<br>
* 和JDK8中的map.forEach不同的是此方法支持index
*
* @param <K> Key类型
* @param <V> Value类型
* @param map {@link Map}
* @param kvConsumer {@link IndexedKVConsumer} 遍历的每条数据处理器
* @param kvConsumer {@link SerConsumer3} 遍历的每条数据处理器
*/
public static <K, V> void forEach(final Map<K, V> map, final IndexedKVConsumer<K, V> kvConsumer) {
public static <K, V> void forEach(final Map<K, V> map, final SerConsumer3<K, V, Integer> kvConsumer) {
if (map == null) {
return;
}
@ -2260,46 +2261,6 @@ public class CollUtil {
iterable.forEach(x -> Optional.ofNullable(map.get(keyGenerate.apply(x))).ifPresent(y -> biConsumer.accept(x, y)));
}
// ---------------------------------------------------------------------------------------------- Interface start
/**
* 针对一个参数做相应的操作<br>
* 此函数接口与JDK8中Consumer不同是多提供了index参数用于标记遍历对象是第几个
*
* @param <T> 处理参数类型
* @author Looly
*/
@FunctionalInterface
public interface IndexedConsumer<T> extends Serializable {
/**
* 接受并处理一个参数
*
* @param value 参数值
* @param index 参数在集合中的索引
*/
void accept(T value, int index);
}
/**
* 针对两个参数做相应的操作例如Map中的KEY和VALUE
*
* @param <K> KEY类型
* @param <V> VALUE类型
* @author Looly
*/
@FunctionalInterface
public interface IndexedKVConsumer<K, V> extends Serializable {
/**
* 接受并处理一对参数
*
* @param key
* @param value
* @param index 参数在集合中的索引
*/
void accept(K key, V value, int index);
}
// ---------------------------------------------------------------------------------------------- Interface end
/**
* 获取Collection或者iterator的大小此方法可以处理的对象类型如下
* <ul>

View File

@ -177,7 +177,8 @@ public class ListUtil {
/**
* 数组转为一个不可变List<br>
* 类似于Java9中的List.of
* 类似于Java9中的List.of<br>
* 不同于Arrays.asList此方法的原数组修改时并不会影响List
*
* @param ts 对象
* @param <T> 对象类型
@ -708,7 +709,7 @@ public class ListUtil {
* @since 6.0.0
*/
@SafeVarargs
public static <T> List<T> splice(List<T> list, int start, int deleteCount, T... items) {
public static <T> List<T> splice(final List<T> list, int start, int deleteCount, final T... items) {
if (CollUtil.isEmpty(list)) {
return zero();
}

View File

@ -98,8 +98,8 @@ public class CompareUtil {
*
* <ul>
* <li>如需对null友好操作如下</li>
* <li><code>Comparator.nullsLast(CompareUtil.natural())</code></li>
* <li><code>Comparator.nullsFirst(CompareUtil.natural())</code></li>
* <li>{@code Comparator.nullsLast(CompareUtil.natural())}</li>
* <li>{@code Comparator.nullsFirst(CompareUtil.natural())}</li>
* </ul>
*
* @param <E> 排序节点类型
@ -115,8 +115,8 @@ public class CompareUtil {
*
* <ul>
* <li>如需对null友好操作如下</li>
* <li><code>Comparator.nullsLast(CompareUtil.naturalReverse())</code></li>
* <li><code>Comparator.nullsFirst(CompareUtil.naturalReverse())</code></li>
* <li>{@code Comparator.nullsLast(CompareUtil.naturalReverse())}</li>
* <li>{@code Comparator.nullsFirst(CompareUtil.naturalReverse())}</li>
* </ul>
*
* @param <E> 排序节点类型
@ -314,7 +314,7 @@ public class CompareUtil {
*
* @param keyExtractor 从对象中提取排序键的函数(参与比较的内容)
* @param atEndIfMiss 如果不在列表中是否排在后边; true:排在后边; false:排在前边
* @param objs 参与排序的数组数组的元素位置决定了对象的排序先后, 示例<code>int[] objs = new int[]{3, 2, 1, 4, 5,6};</code>
* @param objs 参与排序的数组数组的元素位置决定了对象的排序先后, 示例{@code int[] objs = new int[]{3, 2, 1, 4, 5,6};}
* @param <T> 对象类型
* @param <U> 数组对象类型
* @return 索引比较器

View File

@ -0,0 +1,25 @@
package cn.hutool.core.comparator;
import java.util.Comparator;
/**
* 字符串长度比较器短在前
*
* @author looly
* @since 5.8.9
*/
public class StrLengthComparator implements Comparator<CharSequence> {
/**
* 单例的字符串长度比较器短在前
*/
public static final StrLengthComparator INSTANCE = new StrLengthComparator();
@Override
public int compare(final CharSequence o1, final CharSequence o2) {
int result = Integer.compare(o1.length(), o2.length());
if (0 == result) {
result = CompareUtil.compare(o1.toString(), o2.toString());
}
return result;
}
}

View File

@ -2,7 +2,7 @@ package cn.hutool.core.compiler;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.net.URLUtil;
import cn.hutool.core.net.url.URLUtil;
import javax.tools.SimpleJavaFileObject;
import java.io.ByteArrayInputStream;

View File

@ -8,7 +8,7 @@ import cn.hutool.core.io.resource.FileResource;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.io.resource.StringResource;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.URLUtil;
import cn.hutool.core.net.url.URLUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;

View File

@ -2,7 +2,7 @@ package cn.hutool.core.compiler;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.net.URLUtil;
import cn.hutool.core.net.url.URLUtil;
import javax.tools.SimpleJavaFileObject;
import java.io.BufferedInputStream;

View File

@ -533,7 +533,7 @@ public class Convert {
@SuppressWarnings("unchecked")
public static <E extends Enum<E>> E toEnum(final Class<E> clazz, final Object value, final E defaultValue) {
try{
return (E) (new EnumConverter()).convert(clazz, value);
return (E) EnumConverter.INSTANCE.convert(clazz, value);
} catch (final Exception ignore){
return defaultValue;
}
@ -615,7 +615,7 @@ public class Convert {
* @return {@link Map}
* @since 4.6.8
*/
public static <K, V> Map<K, V> toMap(Class<K> keyType, Class<V> valueType, Object value) {
public static <K, V> Map<K, V> toMap(final Class<K> keyType, final Class<V> valueType, final Object value) {
if (value instanceof Map) {
return toMap(value.getClass(), keyType, valueType, value);
} else {
@ -635,7 +635,7 @@ public class Convert {
* @return {@link Map}
*/
@SuppressWarnings({"unchecked"})
public static <K, V> Map<K, V> toMap(Class<?> mapType, Class<K> keyType, Class<V> valueType, Object value) {
public static <K, V> Map<K, V> toMap(final Class<?> mapType, final Class<K> keyType, final Class<V> valueType, final Object value) {
return (Map<K, V>) MapConverter.INSTANCE.convert(mapType, keyType, valueType, value);
}
@ -1049,7 +1049,6 @@ public class Convert {
/**
* 中文大写数字金额转换为数字返回结果以元为单位的BigDecimal类型数字
*
*
* 陆万柒仟伍佰伍拾陆元叁角贰分返回67556.32
* 叁角贰分返回0.32

View File

@ -18,7 +18,7 @@ public interface Converter {
* 如果类型无法确定将读取默认值的类型做为目标类型
*
* @param targetType 目标Type非泛型类使用
* @param value 原始值
* @param value 原始值如果对象实现了此接口则value为this
* @return 转换后的值
* @throws ConvertException 转换无法正常完成或转换异常时抛出此异常
*/

View File

@ -30,6 +30,9 @@ import java.util.Map;
public class BeanConverter implements Converter, Serializable {
private static final long serialVersionUID = 1L;
/**
* 单例对象
*/
public static BeanConverter INSTANCE = new BeanConverter();
private final CopyOptions copyOptions;
@ -51,7 +54,7 @@ public class BeanConverter implements Converter, Serializable {
}
@Override
public Object convert(Type targetType, Object value) throws ConvertException {
public Object convert(final Type targetType, final Object value) throws ConvertException {
Assert.notNull(targetType);
if (null == value) {
return null;
@ -62,7 +65,7 @@ public class BeanConverter implements Converter, Serializable {
return ((Converter) value).convert(targetType, value);
}
Class<?> targetClass = TypeUtil.getClass(targetType);
final Class<?> targetClass = TypeUtil.getClass(targetType);
Assert.notNull(targetClass, "Target type is not a class!");
return convertInternal(targetType, targetClass, value);

View File

@ -17,6 +17,9 @@ import java.util.Calendar;
public class DateConverter extends AbstractConverter {
private static final long serialVersionUID = 1L;
/**
* 单例
*/
public static final DateConverter INSTANCE = new DateConverter();
/**

View File

@ -943,6 +943,24 @@ public class DateTime extends Date {
return this;
}
/**
* 是否为本月最后一天
* @return 是否为本月最后一天
* @since 5.8.8
*/
public boolean isLastDayOfMonth(){
return dayOfMonth() == getLastDayOfMonth();
}
/**
* 获得本月的最后一天
* @return
* @since 5.8.8
*/
public int getLastDayOfMonth(){
return monthEnum().getLastDay(isLeapYear());
}
// -------------------------------------------------------------------- toString start
/**

View File

@ -16,6 +16,7 @@ import cn.hutool.core.math.NumberUtil;
import cn.hutool.core.regex.PatternPool;
import cn.hutool.core.regex.ReUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.CharUtil;
import javax.xml.datatype.XMLGregorianCalendar;
import java.text.DateFormat;
@ -1624,7 +1625,7 @@ public class DateUtil extends CalendarUtil {
return 0;
}
final List<String> hms = StrUtil.splitTrim(timeStr, StrUtil.C_COLON, 3);
final List<String> hms = StrUtil.splitTrim(timeStr, CharUtil.COLON, 3);
final int lastIndex = hms.size() - 1;
int result = 0;
@ -2003,6 +2004,26 @@ public class DateUtil extends CalendarUtil {
return startTime.before(realEndTime) && endTime.after(realStartTime);
}
/**
* 是否为本月最后一天
* @param date {@link Date}
* @return 是否为本月最后一天
* @since 5.8.8
*/
public static boolean isLastDayOfMonth(final Date date){
return date(date).isLastDayOfMonth();
}
/**
* 获得本月的最后一天
* @param date {@link Date}
* @return
* @since 5.8.8
*/
public static int getLastDayOfMonth(final Date date){
return date(date).getLastDayOfMonth();
}
// ------------------------------------------------------------------------ Private method start
/**

View File

@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil;
import cn.hutool.core.reflect.ConstructorUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
@ -201,9 +202,9 @@ public class ExceptionUtil {
*/
public static String stacktraceToOneLineString(final Throwable throwable, final int limit) {
final Map<Character, String> replaceCharToStrMap = new HashMap<>();
replaceCharToStrMap.put(StrUtil.C_CR, StrUtil.SPACE);
replaceCharToStrMap.put(StrUtil.C_LF, StrUtil.SPACE);
replaceCharToStrMap.put(StrUtil.C_TAB, StrUtil.SPACE);
replaceCharToStrMap.put(CharUtil.CR, StrUtil.SPACE);
replaceCharToStrMap.put(CharUtil.LF, StrUtil.SPACE);
replaceCharToStrMap.put(CharUtil.TAB, StrUtil.SPACE);
return stacktraceToString(throwable, limit, replaceCharToStrMap);
}

View File

@ -1,5 +1,6 @@
package cn.hutool.core.io;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.text.StrUtil;
@ -166,9 +167,9 @@ public class BufferUtil {
while (buffer.hasRemaining()) {
b = buffer.get();
charIndex++;
if (b == StrUtil.C_CR) {
if (b == CharUtil.CR) {
canEnd = true;
} else if (b == StrUtil.C_LF) {
} else if (b == CharUtil.LF) {
return canEnd ? charIndex - 2 : charIndex - 1;
} else {
// 只有\r无法确认换行

View File

@ -1,5 +1,6 @@
package cn.hutool.core.io;
import cn.hutool.core.codec.HexUtil;
import cn.hutool.core.text.StrUtil;
import java.io.File;
@ -35,16 +36,35 @@ public class FileTypeUtil {
}
});
FILE_TYPE_MAP.put("ffd8ff", "jpg"); // JPEG (jpg)
FILE_TYPE_MAP.put("89504e47", "png"); // PNG (png)
FILE_TYPE_MAP.put("4749463837", "gif"); // GIF (gif)
FILE_TYPE_MAP.put("4749463839", "gif"); // GIF (gif)
FILE_TYPE_MAP.put("49492a00227105008037", "tif"); // TIFF (tif)
// 2-byte signatures
// https://github.com/sindresorhus/file-type/blob/main/core.js#L90
FILE_TYPE_MAP.put("424d", "bmp"); // 位图(bmp)
FILE_TYPE_MAP.put("424D", "bmp"); // 位图(bmp)
FILE_TYPE_MAP.put("0B77", "ac3");
FILE_TYPE_MAP.put("7801", "dmg");
FILE_TYPE_MAP.put("4D5A", "exe");
FILE_TYPE_MAP.put("1FA0", "Z");
FILE_TYPE_MAP.put("1F9D", "Z");
// 3-byte signatures
// https://github.com/sindresorhus/file-type/blob/main/core.js#L149
FILE_TYPE_MAP.put("474946", "gif"); // GIF (gif)
FILE_TYPE_MAP.put("FFd8FF", "jpg"); // JPEG (jpg)
FILE_TYPE_MAP.put("4949BC", "jxr"); // jxr
FILE_TYPE_MAP.put("1F8B08", "gz"); // gzip
FILE_TYPE_MAP.put("425A68", "bz2"); // bz2
// check string
FILE_TYPE_MAP.put(HexUtil.encodeHexStr("MP+"), "mpc");
FILE_TYPE_MAP.put(HexUtil.encodeHexStr("FLIF"), "flif");
FILE_TYPE_MAP.put(HexUtil.encodeHexStr("8BPS"), "psd");// Photoshop (psd)
FILE_TYPE_MAP.put(HexUtil.encodeHexStr("MPCK"), "mpc");
FILE_TYPE_MAP.put(HexUtil.encodeHexStr("FORM"), "aif");// Musepack, SV8
FILE_TYPE_MAP.put(HexUtil.encodeHexStr("icns"), "icns");
FILE_TYPE_MAP.put("89504e47", "png"); // PNG (png)
FILE_TYPE_MAP.put("49492a00227105008037", "tif"); // TIFF (tif)
FILE_TYPE_MAP.put("41433130313500000000", "dwg"); // CAD (dwg)
FILE_TYPE_MAP.put("7b5c727466315c616e73", "rtf"); // Rich Text Format (rtf)
FILE_TYPE_MAP.put("38425053000100000000", "psd"); // Photoshop (psd)
FILE_TYPE_MAP.put("46726f6d3a203d3f6762", "eml"); // Email [Outlook Express 6] (eml)
FILE_TYPE_MAP.put("5374616E64617264204A", "mdb"); // MS Access (mdb)
FILE_TYPE_MAP.put("252150532D41646F6265", "ps");

View File

@ -13,6 +13,7 @@ import cn.hutool.core.io.file.Tailer;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.io.unit.DataSizeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.func.SerConsumer;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
@ -20,7 +21,7 @@ import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.reflect.ClassUtil;
import cn.hutool.core.regex.ReUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.net.URLUtil;
import cn.hutool.core.net.url.URLUtil;
import cn.hutool.core.compress.ZipUtil;
import java.io.BufferedInputStream;
@ -1306,7 +1307,7 @@ public class FileUtil extends PathUtil {
}
// 给定的路径已经是绝对路径了
return StrUtil.C_SLASH == path.charAt(0) || ReUtil.isMatch(PATTERN_PATH_ABSOLUTE, path);
return CharUtil.SLASH == path.charAt(0) || ReUtil.isMatch(PATTERN_PATH_ABSOLUTE, path);
}
/**
@ -1600,7 +1601,7 @@ public class FileUtil extends PathUtil {
if (prefixIndex > -1) {
// 可能Windows风格路径
prefix = pathToUse.substring(0, prefixIndex + 1);
if (StrUtil.startWith(prefix, StrUtil.C_SLASH)) {
if (StrUtil.startWith(prefix, CharUtil.SLASH)) {
// 去除类似于/C:这类路径开头的斜杠
prefix = prefix.substring(1);
}
@ -1616,7 +1617,7 @@ public class FileUtil extends PathUtil {
pathToUse = pathToUse.substring(1);
}
final List<String> pathList = StrUtil.split(pathToUse, StrUtil.C_SLASH);
final List<String> pathList = StrUtil.split(pathToUse, CharUtil.SLASH);
final List<String> pathElements = new LinkedList<>();
int tops = 0;
@ -1887,6 +1888,7 @@ public class FileUtil extends PathUtil {
*/
public static BOMInputStream getBOMInputStream(final File file) throws IORuntimeException {
try {
//noinspection IOStreamConstructor
return new BOMInputStream(new FileInputStream(file));
} catch (final IOException e) {
throw new IORuntimeException(e);
@ -2268,10 +2270,10 @@ public class FileUtil extends PathUtil {
* 按行处理文件内容编码为UTF-8
*
* @param file 文件
* @param lineHandler {@link LineHandler}行处理器
* @param lineHandler {@link SerConsumer}行处理器
* @throws IORuntimeException IO异常
*/
public static void readUtf8Lines(final File file, final LineHandler lineHandler) throws IORuntimeException {
public static void readUtf8Lines(final File file, final SerConsumer<String> lineHandler) throws IORuntimeException {
readLines(file, CharsetUtil.UTF_8, lineHandler);
}
@ -2280,10 +2282,10 @@ public class FileUtil extends PathUtil {
*
* @param file 文件
* @param charset 编码
* @param lineHandler {@link LineHandler}行处理器
* @param lineHandler {@link SerConsumer}行处理器
* @throws IORuntimeException IO异常
*/
public static void readLines(final File file, final Charset charset, final LineHandler lineHandler) throws IORuntimeException {
public static void readLines(final File file, final Charset charset, final SerConsumer<String> lineHandler) throws IORuntimeException {
FileReader.of(file, charset).readLines(lineHandler);
}
@ -2292,15 +2294,15 @@ public class FileUtil extends PathUtil {
*
* @param file {@link RandomAccessFile}文件
* @param charset 编码
* @param lineHandler {@link LineHandler}行处理器
* @param lineHandler {@link SerConsumer}行处理器
* @throws IORuntimeException IO异常
* @since 4.5.2
*/
public static void readLines(final RandomAccessFile file, final Charset charset, final LineHandler lineHandler) {
public static void readLines(final RandomAccessFile file, final Charset charset, final SerConsumer<String> lineHandler) {
String line;
try {
while ((line = file.readLine()) != null) {
lineHandler.handle(CharsetUtil.convert(line, CharsetUtil.ISO_8859_1, charset));
lineHandler.accept(CharsetUtil.convert(line, CharsetUtil.ISO_8859_1, charset));
}
} catch (final IOException e) {
throw new IORuntimeException(e);
@ -2312,14 +2314,14 @@ public class FileUtil extends PathUtil {
*
* @param file {@link RandomAccessFile}文件
* @param charset 编码
* @param lineHandler {@link LineHandler}行处理器
* @param lineHandler {@link SerConsumer}行处理器
* @throws IORuntimeException IO异常
* @since 4.5.2
*/
public static void readLine(final RandomAccessFile file, final Charset charset, final LineHandler lineHandler) {
public static void readLine(final RandomAccessFile file, final Charset charset, final SerConsumer<String> lineHandler) {
final String line = readLine(file, charset);
if (null != line) {
lineHandler.handle(line);
lineHandler.accept(line);
}
}
@ -2431,6 +2433,7 @@ public class FileUtil extends PathUtil {
public static BufferedOutputStream getOutputStream(final File file) throws IORuntimeException {
final OutputStream out;
try {
//noinspection IOStreamConstructor
out = new FileOutputStream(touch(file));
} catch (final IOException e) {
throw new IORuntimeException(e);
@ -3277,6 +3280,8 @@ public class FileUtil extends PathUtil {
contentType = "application/x-rar-compressed";
} else if (StrUtil.endWithIgnoreCase(filePath, ".7z")) {
contentType = "application/x-7z-compressed";
} else if (StrUtil.endWithIgnoreCase(filePath, ".wgt")) {
contentType = "application/widget";
}
}
@ -3348,7 +3353,7 @@ public class FileUtil extends PathUtil {
* @param file 文件
* @param handler 行处理器
*/
public static void tail(final File file, final LineHandler handler) {
public static void tail(final File file, final SerConsumer<String> handler) {
tail(file, CharsetUtil.UTF_8, handler);
}
@ -3360,7 +3365,7 @@ public class FileUtil extends PathUtil {
* @param charset 编码
* @param handler 行处理器
*/
public static void tail(final File file, final Charset charset, final LineHandler handler) {
public static void tail(final File file, final Charset charset, final SerConsumer<String> handler) {
new Tailer(file, charset, handler).start();
}

View File

@ -6,6 +6,7 @@ import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.copy.ReaderWriterCopier;
import cn.hutool.core.io.copy.StreamCopier;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.func.SerConsumer;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.codec.HexUtil;
import cn.hutool.core.text.StrUtil;
@ -621,7 +622,7 @@ public class IoUtil extends NioUtil {
* @throws IORuntimeException IO异常
*/
public static <T extends Collection<String>> T readLines(final Reader reader, final T collection) throws IORuntimeException {
readLines(reader, (LineHandler) collection::add);
readLines(reader, (SerConsumer<String>) collection::add);
return collection;
}
@ -633,7 +634,7 @@ public class IoUtil extends NioUtil {
* @throws IORuntimeException IO异常
* @since 3.1.1
*/
public static void readUtf8Lines(final InputStream in, final LineHandler lineHandler) throws IORuntimeException {
public static void readUtf8Lines(final InputStream in, final SerConsumer<String> lineHandler) throws IORuntimeException {
readLines(in, CharsetUtil.UTF_8, lineHandler);
}
@ -646,7 +647,7 @@ public class IoUtil extends NioUtil {
* @throws IORuntimeException IO异常
* @since 3.0.9
*/
public static void readLines(final InputStream in, final Charset charset, final LineHandler lineHandler) throws IORuntimeException {
public static void readLines(final InputStream in, final Charset charset, final SerConsumer<String> lineHandler) throws IORuntimeException {
readLines(getReader(in, charset), lineHandler);
}
@ -659,12 +660,12 @@ public class IoUtil extends NioUtil {
* @param lineHandler 行处理接口实现handle方法用于编辑一行的数据后入到指定地方
* @throws IORuntimeException IO异常
*/
public static void readLines(final Reader reader, final LineHandler lineHandler) throws IORuntimeException {
public static void readLines(final Reader reader, final SerConsumer<String> lineHandler) throws IORuntimeException {
Assert.notNull(reader);
Assert.notNull(lineHandler);
for (final String line : lineIter(reader)) {
lineHandler.handle(line);
lineHandler.accept(line);
}
}

View File

@ -1,15 +0,0 @@
package cn.hutool.core.io;
/**
* 行处理器
* @author Looly
*
*/
@FunctionalInterface
public interface LineHandler {
/**
* 处理一行数据可以编辑后存入指定地方
* @param line
*/
void handle(String line);
}

View File

@ -18,7 +18,7 @@ public class NullOutputStream extends OutputStream {
public static final NullOutputStream NULL_OUTPUT_STREAM = new NullOutputStream();
/**
* 什么也不做写出到<code>/dev/null</code>.
* 什么也不做写出到{@code /dev/null}.
*
* @param b 写出的数据
* @param off 开始位置
@ -30,7 +30,7 @@ public class NullOutputStream extends OutputStream {
}
/**
* 什么也不做写出到 <code>/dev/null</code>.
* 什么也不做写出到 {@code /dev/null}.
*
* @param b 写出的数据
*/
@ -40,7 +40,7 @@ public class NullOutputStream extends OutputStream {
}
/**
* 什么也不做写出到 <code>/dev/null</code>.
* 什么也不做写出到 {@code /dev/null}.
*
* @param b 写出的数据
* @throws IOException 不抛出

View File

@ -3,9 +3,9 @@ package cn.hutool.core.io.file;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.LineHandler;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.lang.func.SerConsumer;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.CharsetUtil;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
@ -179,7 +179,7 @@ public class FileReader extends FileWrapper {
* @throws IORuntimeException IO异常
* @since 3.0.9
*/
public void readLines(final LineHandler lineHandler) throws IORuntimeException{
public void readLines(final SerConsumer<String> lineHandler) throws IORuntimeException{
BufferedReader reader = null;
try {
reader = FileUtil.getReader(file, charset);

View File

@ -1,20 +1,21 @@
package cn.hutool.core.io.file;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.func.Wrapper;
import cn.hutool.core.util.CharsetUtil;
import java.io.File;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
/**
* 文件包装器扩展文件对象
*
* @author Looly
*
*/
public class FileWrapper implements Serializable{
public class FileWrapper implements Wrapper<File>, Serializable{
private static final long serialVersionUID = 1L;
protected File file;
@ -40,7 +41,8 @@ public class FileWrapper implements Serializable{
* 获得文件
* @return 文件
*/
public File getFile() {
@Override
public File getRaw() {
return file;
}

View File

@ -2,8 +2,8 @@ package cn.hutool.core.io.file;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.LineHandler;
import cn.hutool.core.io.watch.SimpleWatcher;
import cn.hutool.core.lang.func.SerConsumer;
import java.io.IOException;
import java.io.RandomAccessFile;
@ -21,16 +21,16 @@ public class LineReadWatcher extends SimpleWatcher implements Runnable {
private final RandomAccessFile randomAccessFile;
private final Charset charset;
private final LineHandler lineHandler;
private final SerConsumer<String> lineHandler;
/**
* 构造
*
* @param randomAccessFile {@link RandomAccessFile}
* @param charset 编码
* @param lineHandler 行处理器{@link LineHandler}实现
* @param lineHandler 行处理器{@link SerConsumer}实现
*/
public LineReadWatcher(final RandomAccessFile randomAccessFile, final Charset charset, final LineHandler lineHandler) {
public LineReadWatcher(final RandomAccessFile randomAccessFile, final Charset charset, final SerConsumer<String> lineHandler) {
this.randomAccessFile = randomAccessFile;
this.charset = charset;
this.lineHandler = lineHandler;
@ -45,7 +45,7 @@ public class LineReadWatcher extends SimpleWatcher implements Runnable {
public void onModify(final WatchEvent<?> event, final Path currentPath) {
final RandomAccessFile randomAccessFile = this.randomAccessFile;
final Charset charset = this.charset;
final LineHandler lineHandler = this.lineHandler;
final SerConsumer<String> lineHandler = this.lineHandler;
try {
final long currentLength = randomAccessFile.length();

View File

@ -4,8 +4,8 @@ import cn.hutool.core.date.DateUnit;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.LineHandler;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.func.SerConsumer;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
@ -30,12 +30,15 @@ import java.util.concurrent.TimeUnit;
public class Tailer implements Serializable {
private static final long serialVersionUID = 1L;
public static final LineHandler CONSOLE_HANDLER = new ConsoleLineHandler();
/**
* 控制台打印的处理类
*/
public static final SerConsumer<String> CONSOLE_HANDLER = new ConsoleLineHandler();
/** 编码 */
private final Charset charset;
/** 行处理器 */
private final LineHandler lineHandler;
private final SerConsumer<String> lineHandler;
/** 初始读取的行数 */
private final int initReadLine;
/** 定时任务检查间隔时长 */
@ -50,7 +53,7 @@ public class Tailer implements Serializable {
* @param file 文件
* @param lineHandler 行处理器
*/
public Tailer(final File file, final LineHandler lineHandler) {
public Tailer(final File file, final SerConsumer<String> lineHandler) {
this(file, lineHandler, 0);
}
@ -61,7 +64,7 @@ public class Tailer implements Serializable {
* @param lineHandler 行处理器
* @param initReadLine 启动时预读取的行数
*/
public Tailer(final File file, final LineHandler lineHandler, final int initReadLine) {
public Tailer(final File file, final SerConsumer<String> lineHandler, final int initReadLine) {
this(file, CharsetUtil.UTF_8, lineHandler, initReadLine, DateUnit.SECOND.getMillis());
}
@ -72,7 +75,7 @@ public class Tailer implements Serializable {
* @param charset 编码
* @param lineHandler 行处理器
*/
public Tailer(final File file, final Charset charset, final LineHandler lineHandler) {
public Tailer(final File file, final Charset charset, final SerConsumer<String> lineHandler) {
this(file, charset, lineHandler, 0, DateUnit.SECOND.getMillis());
}
@ -85,7 +88,7 @@ public class Tailer implements Serializable {
* @param initReadLine 启动时预读取的行数
* @param period 检查间隔
*/
public Tailer(final File file, final Charset charset, final LineHandler lineHandler, final int initReadLine, final long period) {
public Tailer(final File file, final Charset charset, final SerConsumer<String> lineHandler, final int initReadLine, final long period) {
checkFile(file);
this.charset = charset;
this.lineHandler = lineHandler;
@ -188,7 +191,7 @@ public class Tailer implements Serializable {
// 输出缓存栈中的内容
while (false == stack.isEmpty()) {
this.lineHandler.handle(stack.pop());
this.lineHandler.accept(stack.pop());
}
}
@ -221,9 +224,10 @@ public class Tailer implements Serializable {
* @author looly
* @since 4.5.2
*/
public static class ConsoleLineHandler implements LineHandler {
public static class ConsoleLineHandler implements SerConsumer<String> {
private static final long serialVersionUID = 1L;
@Override
public void handle(final String line) {
public void accepting(final String line) {
Console.log(line);
}
}

View File

@ -3,7 +3,7 @@ package cn.hutool.core.io.resource;
import cn.hutool.core.classloader.ClassLoaderUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.net.URLUtil;
import cn.hutool.core.net.url.URLUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ObjUtil;

View File

@ -3,7 +3,7 @@ package cn.hutool.core.io.resource;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.net.URLUtil;
import cn.hutool.core.net.url.URLUtil;
import java.io.File;
import java.io.InputStream;

View File

@ -7,9 +7,10 @@ import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.classloader.ClassLoaderUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.net.URLUtil;
import cn.hutool.core.net.url.URLUtil;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
@ -210,4 +211,26 @@ public class ResourceUtil {
}
return new ClassPathResource(path);
}
/**
* 获取{@link UrlResource} 资源对象
*
* @param url URL
* @return {@link Resource} 资源对象
* @since 6.0.0
*/
public static Resource getResource(final URL url) {
return new UrlResource(url);
}
/**
* 获取{@link FileResource} 资源对象
*
* @param file {@link File}
* @return {@link Resource} 资源对象
* @since 6.0.0
*/
public static Resource getResource(final File file) {
return new FileResource(file);
}
}

View File

@ -2,7 +2,7 @@ package cn.hutool.core.io.resource;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.net.URLUtil;
import cn.hutool.core.net.url.URLUtil;
import java.io.File;
import java.io.InputStream;

View File

@ -1,24 +0,0 @@
package cn.hutool.core.io.watch;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
/**
* 监听事件处理函数接口
*
* @author looly
* @since 5.4.0
*/
@FunctionalInterface
public interface WatchAction {
/**
* 事件处理通过实现此方法处理各种事件
*
* 事件可以调用 {@link WatchEvent#kind()}获取对应事件见{@link WatchKind}
*
* @param event 事件
* @param currentPath 事件发生的当前Path路径
*/
void doAction(WatchEvent<?> event, Path currentPath);
}

View File

@ -4,7 +4,8 @@ import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.watch.watchers.WatcherChain;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.net.URLUtil;
import cn.hutool.core.net.url.URLUtil;
import cn.hutool.core.util.CharUtil;
import java.io.File;
import java.io.IOException;
@ -322,7 +323,7 @@ public class WatchMonitor extends WatchServer {
if (null != lastPathEle) {
final String lastPathEleStr = lastPathEle.toString();
//带有点表示有扩展名按照未创建的文件对待Linux下.d的为目录排除之
if (StrUtil.contains(lastPathEleStr, StrUtil.C_DOT) && false == StrUtil.endWithIgnoreCase(lastPathEleStr, ".d")) {
if (StrUtil.contains(lastPathEleStr, CharUtil.DOT) && false == StrUtil.endWithIgnoreCase(lastPathEleStr, ".d")) {
this.filePath = this.path;
this.path = this.filePath.getParent();
}

View File

@ -1,6 +1,7 @@
package cn.hutool.core.io.watch;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.func.SerBiConsumer;
import cn.hutool.core.util.ArrayUtil;
import java.io.Closeable;
@ -132,7 +133,7 @@ public class WatchServer extends Thread implements Closeable, Serializable {
* @param watchFilter 监听过滤接口通过实现此接口过滤掉不需要监听的情况{@link Predicate#test(Object)}{@code true}保留null表示不过滤
* @since 5.4.0
*/
public void watch(final WatchAction action, final Predicate<WatchEvent<?>> watchFilter) {
public void watch(final SerBiConsumer<WatchEvent<?>, Path> action, final Predicate<WatchEvent<?>> watchFilter) {
final WatchKey wk;
try {
wk = watchService.take();
@ -150,7 +151,7 @@ public class WatchServer extends Thread implements Closeable, Serializable {
continue;
}
action.doAction(event, currentPath);
action.accept(event, currentPath);
}
wk.reset();

View File

@ -1,7 +1,7 @@
package cn.hutool.core.io.watch;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.net.URLUtil;
import cn.hutool.core.net.url.URLUtil;
import java.io.File;
import java.io.IOException;

View File

@ -30,7 +30,7 @@ public class Ansi8BitColor implements AnsiElement {
* @param code 颜色代码(0-255)
* @return 前景色ANSI颜色实例
*/
public static Ansi8BitColor foreground(int code) {
public static Ansi8BitColor foreground(final int code) {
return new Ansi8BitColor(PREFIX_FORE, code);
}
@ -40,7 +40,7 @@ public class Ansi8BitColor implements AnsiElement {
* @param code 颜色代码(0-255)
* @return 背景色ANSI颜色实例
*/
public static Ansi8BitColor background(int code) {
public static Ansi8BitColor background(final int code) {
return new Ansi8BitColor(PREFIX_BACK, code);
}
@ -52,9 +52,9 @@ public class Ansi8BitColor implements AnsiElement {
*
* @param prefix 前缀
* @param code 颜色代码(0-255)
* @throws IllegalArgumentException 颜色代码不在0~255范围内
* @throws IllegalArgumentException 颜色代码不在 0~255 范围内
*/
private Ansi8BitColor(String prefix, int code) {
private Ansi8BitColor(final String prefix, final int code) {
Assert.isTrue(code >= 0 && code <= 255, "Code must be between 0 and 255");
this.prefix = prefix;
this.code = code;
@ -95,14 +95,14 @@ public class Ansi8BitColor implements AnsiElement {
}
@Override
public boolean equals(Object obj) {
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Ansi8BitColor other = (Ansi8BitColor) obj;
final Ansi8BitColor other = (Ansi8BitColor) obj;
return ObjUtil.equals(this.prefix, other.prefix) && this.code == other.code;
}

View File

@ -1,4 +1,4 @@
package cn.hutool.core.builder;
package cn.hutool.core.lang.builder;
import java.io.Serializable;
@ -16,4 +16,4 @@ public interface Builder<T> extends Serializable{
* @return 被构建的对象
*/
T build();
}
}

View File

@ -1,4 +1,4 @@
package cn.hutool.core.builder;
package cn.hutool.core.lang.builder;
import cn.hutool.core.lang.func.SerConsumer3;
@ -55,6 +55,7 @@ import java.util.function.Supplier;
*
* @author TomXin VampireAchao
* @since 5.7.21
* @param <T> 构建对象类型
*/
public class GenericBuilder<T> implements Builder<T> {
private static final long serialVersionUID = 1L;

View File

@ -5,4 +5,4 @@
* @author looly
*
*/
package cn.hutool.core.builder;
package cn.hutool.core.lang.builder;

View File

@ -1,5 +1,6 @@
package cn.hutool.core.lang.func;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.classloader.ClassLoaderUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.StrPool;
@ -11,28 +12,42 @@ import java.lang.reflect.Method;
import java.lang.reflect.Type;
/**
* 存放lambda信息
* 存放lambda信息<br>
* 此类是{@link SerializedLambda}信息的扩充和补充类包括
* <ul>
* <li>实例化后的对象方法参数类型一般用于方法引用</li>
* </ul>
*
* @author VampireAchao
*/
public class LambdaInfo {
private static final Type[] EMPTY_TYPE = new Type[0];
// 实例对象的方法参数类型
private final Type[] instantiatedMethodParameterTypes;
// 方法或构造的参数类型
private final Type[] parameterTypes;
private final Type returnType;
// 方法名或构造名称
private final String name;
private final Executable executable;
private final Class<?> clazz;
private final SerializedLambda lambda;
/**
* 构造
*
* @param executable 构造对象{@link Constructor}或方法对象{@link Method}
* @param lambda 实现了序列化接口的lambda表达式
*/
public LambdaInfo(final Executable executable, final SerializedLambda lambda) {
Assert.notNull(executable, "executable must be not null!");
// return type
final boolean isMethod = executable instanceof Method;
final boolean isConstructor = executable instanceof Constructor;
Assert.isTrue(isMethod || isConstructor, "Unsupported executable type: " + executable.getClass());
this.returnType = isMethod ?
((Method)executable).getGenericReturnType() : ((Constructor<?>)executable).getDeclaringClass();
((Method) executable).getGenericReturnType() : ((Constructor<?>) executable).getDeclaringClass();
// lambda info
this.parameterTypes = executable.getGenericParameterTypes();
@ -42,15 +57,89 @@ public class LambdaInfo {
this.lambda = lambda;
// types
final int index = lambda.getInstantiatedMethodType().indexOf(";)");
this.instantiatedMethodParameterTypes = (index > -1) ? getInstantiatedMethodParamTypes(lambda, index) : EMPTY_TYPE;
final String instantiatedMethodType = lambda.getInstantiatedMethodType();
final int index = instantiatedMethodType.indexOf(";)");
this.instantiatedMethodParameterTypes = (index > -1) ?
getInstantiatedMethodParamTypes(instantiatedMethodType.substring(1, index + 1)) : EMPTY_TYPE;
}
/**
* 实例方法参数类型
*
* @return 实例方法参数类型
*/
public Type[] getInstantiatedMethodParameterTypes() {
return instantiatedMethodParameterTypes;
}
/**
* 获得构造或方法参数类型列表
*
* @return 参数类型列表
*/
public Type[] getParameterTypes() {
return parameterTypes;
}
/**
* 获取返回值类型方法引用
*
* @return 返回值类型
*/
public Type getReturnType() {
return returnType;
}
/**
* 方法或构造名称
*
* @return 方法或构造名称
*/
public String getName() {
return name;
}
/**
* 字段名称主要用于方法名称截取方法名称必须为getXXXisXXXsetXXX
*
* @return getter或setter对应的字段名称
*/
public String getFieldName() {
return BeanUtil.getFieldName(getName());
}
/**
* 方法或构造对象
*
* @return 方法或构造对象
*/
public Executable getExecutable() {
return executable;
}
/**
* 方法或构造所在类
*
* @return 方法或构造所在类
*/
public Class<?> getClazz() {
return clazz;
}
/**
* 获得Lambda表达式对象
*
* @return 获得Lambda表达式对象
*/
public SerializedLambda getLambda() {
return lambda;
}
/**
* 根据lambda对象的方法签名信息解析获得实际的参数类型
*/
private Type[] getInstantiatedMethodParamTypes(SerializedLambda lambda, int index) {
final String className = lambda.getInstantiatedMethodType().substring(1, index + 1);
private static Type[] getInstantiatedMethodParamTypes(final String className) {
final String[] instantiatedTypeNames = className.split(";");
final Type[] types = new Type[instantiatedTypeNames.length];
for (int i = 0; i < instantiatedTypeNames.length; i++) {
@ -72,32 +161,4 @@ public class LambdaInfo {
}
return types;
}
public Type[] getInstantiatedMethodParameterTypes() {
return instantiatedMethodParameterTypes;
}
public Type[] getParameterTypes() {
return parameterTypes;
}
public Type getReturnType() {
return returnType;
}
public String getName() {
return name;
}
public Executable getExecutable() {
return executable;
}
public Class<?> getClazz() {
return clazz;
}
public SerializedLambda getLambda() {
return lambda;
}
}

View File

@ -13,7 +13,6 @@ import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Objects;
/**
* Lambda相关工具类
@ -23,33 +22,35 @@ import java.util.Objects;
*/
public class LambdaUtil {
private static final WeakConcurrentMap<String, LambdaInfo> CACHE = new WeakConcurrentMap<>();
private static final WeakConcurrentMap<Object, LambdaInfo> CACHE = new WeakConcurrentMap<>();
/**
* 通过对象的方法或类的静态方法引用获取lambda实现类
* 传入lambda无参数但含有返回值的情况能够匹配到此方法
* <ul>
* <li>引用特定对象的实例方法<pre>{@code
* MyTeacher myTeacher = new MyTeacher();
* Class<MyTeacher> supplierClass = LambdaUtil.getRealClass(myTeacher::getAge);
* Assert.assertEquals(MyTeacher.class, supplierClass);
* }</pre></li>
* <li>引用静态无参方法<pre>{@code
* Class<MyTeacher> staticSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeAge);
* Assert.assertEquals(MyTeacher.class, staticSupplierClass);
* }</pre></li>
* <li>引用特定对象的实例方法<pre>{@code
* MyTeacher myTeacher = new MyTeacher();
* Class<MyTeacher> supplierClass = LambdaUtil.getRealClass(myTeacher::getAge);
* Assert.assertEquals(MyTeacher.class, supplierClass);
* }</pre>
* </li>
* <li>引用静态无参方法<pre>{@code
* Class<MyTeacher> staticSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeAge);
* Assert.assertEquals(MyTeacher.class, staticSupplierClass);
* }</pre>
* </li>
* </ul>
* 在以下场景无法获取到正确类型
* <pre>{@code
* // 枚举测试只能获取到枚举类型
* Class<Enum<?>> enumSupplierClass = LambdaUtil.getRealClass(LambdaUtil.LambdaKindEnum.REF_NONE::ordinal);
* Assert.assertEquals(Enum.class, enumSupplierClass);
* // 调用父类方法只能获取到父类类型
* Class<Entity<?>> superSupplierClass = LambdaUtil.getRealClass(myTeacher::getId);
* Assert.assertEquals(Entity.class, superSupplierClass);
* // 引用父类静态带参方法只能获取到父类类型
* Class<Entity<?>> staticSuperFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeId);
* Assert.assertEquals(Entity.class, staticSuperFunctionClass);
* // 枚举测试只能获取到枚举类型
* Class<Enum<?>> enumSupplierClass = LambdaUtil.getRealClass(LambdaUtil.LambdaKindEnum.REF_NONE::ordinal);
* Assert.assertEquals(Enum.class, enumSupplierClass);
* // 调用父类方法只能获取到父类类型
* Class<Entity<?>> superSupplierClass = LambdaUtil.getRealClass(myTeacher::getId);
* Assert.assertEquals(Entity.class, superSupplierClass);
* // 引用父类静态带参方法只能获取到父类类型
* Class<Entity<?>> staticSuperFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeId);
* Assert.assertEquals(Entity.class, staticSuperFunctionClass);
* }</pre>
*
* @param func lambda
@ -61,7 +62,10 @@ public class LambdaUtil {
@SuppressWarnings("unchecked")
public static <R, T extends Serializable> Class<R> getRealClass(final T func) {
final LambdaInfo lambdaInfo = resolve(func);
return (Class<R>) Opt.of(lambdaInfo).map(LambdaInfo::getInstantiatedMethodParameterTypes).filter(types -> types.length != 0).map(types -> types[types.length - 1]).orElseGet(lambdaInfo::getClazz);
return (Class<R>) Opt.of(lambdaInfo)
.map(LambdaInfo::getInstantiatedMethodParameterTypes)
.filter(types -> types.length != 0).map(types -> types[types.length - 1])
.orElseGet(lambdaInfo::getClazz);
}
/**
@ -73,16 +77,10 @@ public class LambdaUtil {
* @return 返回解析后的结果
*/
public static <T extends Serializable> LambdaInfo resolve(final T func) {
return CACHE.computeIfAbsent(func.getClass().getName(), (key) -> {
return CACHE.computeIfAbsent(func, (key) -> {
final SerializedLambda serializedLambda = _resolve(func);
final String methodName = serializedLambda.getImplMethodName();
final Class<?> implClass;
ClassLoaderUtil.loadClass(serializedLambda.getImplClass().replace("/", "."), true);
try {
implClass = Class.forName(serializedLambda.getImplClass().replace("/", "."), true, Thread.currentThread().getContextClassLoader());
} catch (final ClassNotFoundException e) {
throw new UtilException(e);
}
final Class<?> implClass = ClassLoaderUtil.loadClass(serializedLambda.getImplClass(), true);
if ("<init>".equals(methodName)) {
for (final Constructor<?> constructor : implClass.getDeclaredConstructors()) {
if (ReflectUtil.getDescriptor(constructor).equals(serializedLambda.getImplMethodSignature())) {
@ -160,7 +158,7 @@ public class LambdaUtil {
throw new IllegalArgumentException("Not a lambda expression: " + clazz.getName());
}
final Object serLambda = MethodUtil.invoke(func, "writeReplace");
if (Objects.nonNull(serLambda) && serLambda instanceof SerializedLambda) {
if (serLambda instanceof SerializedLambda) {
return (SerializedLambda) serLambda;
}
throw new UtilException("writeReplace result value is not java.lang.invoke.SerializedLambda");

View File

@ -10,6 +10,8 @@ import java.util.stream.Stream;
/**
* SerBiConsumer
*
* @param <T> 第一个参数类型
* @param <U> 第二个参数类型
* @author VampireAchao
*/
@FunctionalInterface
@ -24,7 +26,8 @@ public interface SerBiConsumer<T, U> extends BiConsumer<T, U>, Serializable {
*/
@SafeVarargs
static <T, U> SerBiConsumer<T, U> multi(final SerBiConsumer<T, U>... consumers) {
return Stream.of(consumers).reduce(SerBiConsumer::andThen).orElseGet(() -> (o, q) -> {});
return Stream.of(consumers).reduce(SerBiConsumer::andThen).orElseGet(() -> (o, q) -> {
});
}
/**
@ -79,6 +82,7 @@ public interface SerBiConsumer<T, U> extends BiConsumer<T, U>, Serializable {
* @return 什么也不做
*/
static <T, U> SerBiConsumer<T, U> nothing() {
return (l, r) -> {};
return (l, r) -> {
};
}
}

View File

@ -30,10 +30,10 @@ public interface SerFunction<T, R> extends Function<T, R>, Serializable {
* @return the function result
*/
@Override
default R apply(T t) {
default R apply(final T t) {
try {
return applying(t);
} catch (Exception e) {
} catch (final Exception e) {
throw new UtilException(e);
}
}

View File

@ -16,12 +16,12 @@ import java.util.stream.Stream;
public interface SerRunnable extends Runnable, Serializable {
/**
* When an object implementing interface <code>Runnable</code> is used
* When an object implementing interface {@code Runnable} is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* {@code run} method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* The general contract of the method {@code run} is that it may
* take any action whatsoever.
*
* @throws Exception wrapped checked exceptions
@ -30,12 +30,12 @@ public interface SerRunnable extends Runnable, Serializable {
void running() throws Exception;
/**
* When an object implementing interface <code>Runnable</code> is used
* When an object implementing interface {@code Runnable} is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* {@code run} method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* The general contract of the method {@code run} is that it may
* take any action whatsoever.
*
* @see Thread#run()

View File

@ -11,9 +11,10 @@ import java.util.stream.Stream;
*
* @author VampireAchao
* @see Supplier
* @param <R> 返回值类型
*/
@FunctionalInterface
public interface SerSupplier<T> extends Supplier<T>, Serializable {
public interface SerSupplier<R> extends Supplier<R>, Serializable {
/**
* Gets a result.
@ -21,7 +22,7 @@ public interface SerSupplier<T> extends Supplier<T>, Serializable {
* @return a result
* @throws Exception wrapped checked exceptions
*/
T getting() throws Exception;
R getting() throws Exception;
/**
* Gets a result.
@ -29,7 +30,7 @@ public interface SerSupplier<T> extends Supplier<T>, Serializable {
* @return a result
*/
@Override
default T get() {
default R get() {
try {
return getting();
} catch (final Exception e) {

View File

@ -0,0 +1,17 @@
package cn.hutool.core.lang.func;
/**
* 包装接口
*
* @param <T> 原始对象类型
* @author looly
* @since 6.0.0
*/
public interface Wrapper<T> {
/**
* 获取原始对象
*
* @return 原始对象
*/
T getRaw();
}

View File

@ -1,102 +0,0 @@
package cn.hutool.core.lang.getter;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* 数组类型的Get接口
* @author Looly
*
*/
public interface ArrayTypeGetter {
/*-------------------------- 数组类型 start -------------------------------*/
/**
* 获取Object型属性值数组
*
* @param key 属性名
* @return 属性值列表
*/
String[] getObjs(String key);
/**
* 获取String型属性值数组
*
* @param key 属性名
* @return 属性值列表
*/
String[] getStrs(String key);
/**
* 获取Integer型属性值数组
*
* @param key 属性名
* @return 属性值列表
*/
Integer[] getInts(String key);
/**
* 获取Short型属性值数组
*
* @param key 属性名
* @return 属性值列表
*/
Short[] getShorts(String key);
/**
* 获取Boolean型属性值数组
*
* @param key 属性名
* @return 属性值列表
*/
Boolean[] getBools(String key);
/**
* 获取Long型属性值数组
*
* @param key 属性名
* @return 属性值列表
*/
Long[] getLongs(String key);
/**
* 获取Character型属性值数组
*
* @param key 属性名
* @return 属性值列表
*/
Character[] getChars(String key);
/**
* 获取Double型属性值数组
*
* @param key 属性名
* @return 属性值列表
*/
Double[] getDoubles(String key);
/**
* 获取Byte型属性值数组
*
* @param key 属性名
* @return 属性值列表
*/
Byte[] getBytes(String key);
/**
* 获取BigInteger型属性值数组
*
* @param key 属性名
* @return 属性值列表
*/
BigInteger[] getBigIntegers(String key);
/**
* 获取BigDecimal型属性值数组
*
* @param key 属性名
* @return 属性值列表
*/
BigDecimal[] getBigDecimals(String key);
/*-------------------------- 数组类型 end -------------------------------*/
}

View File

@ -1,130 +0,0 @@
package cn.hutool.core.lang.getter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
/**
* 基本类型的getter接口<br>
* 提供一个统一的接口定义返回不同类型的值基本类型<br>
*
* @author Looly
*/
public interface BasicTypeGetter<K> {
/*-------------------------- 基本类型 start -------------------------------*/
/**
* 获取Object属性值
*
* @param key 属性名
* @return 属性值
*/
Object getObj(K key);
/**
* 获取字符串型属性值
*
* @param key 属性名
* @return 属性值
*/
String getStr(K key);
/**
* 获取int型属性值
*
* @param key 属性名
* @return 属性值
*/
Integer getInt(K key);
/**
* 获取short型属性值
*
* @param key 属性名
* @return 属性值
*/
Short getShort(K key);
/**
* 获取boolean型属性值
*
* @param key 属性名
* @return 属性值
*/
Boolean getBool(K key);
/**
* 获取long型属性值
*
* @param key 属性名
* @return 属性值
*/
Long getLong(K key);
/**
* 获取char型属性值
*
* @param key 属性名
* @return 属性值
*/
Character getChar(K key);
/**
* 获取float型属性值<br>
*
* @param key 属性名
* @return 属性值
*/
Float getFloat(K key);
/**
* 获取double型属性值
*
* @param key 属性名
* @return 属性值
*/
Double getDouble(K key);
/**
* 获取byte型属性值
*
* @param key 属性名
* @return 属性值
*/
Byte getByte(K key);
/**
* 获取BigDecimal型属性值
*
* @param key 属性名
* @return 属性值
*/
BigDecimal getBigDecimal(K key);
/**
* 获取BigInteger型属性值
*
* @param key 属性名
* @return 属性值
*/
BigInteger getBigInteger(K key);
/**
* 获得Enum类型的值
*
* @param <E> 枚举类型
* @param clazz Enum的Class
* @param key KEY
* @return Enum类型的值无则返回Null
*/
<E extends Enum<E>> E getEnum(Class<E> clazz, K key);
/**
* 获取Date类型值
*
* @param key 属性名
* @return Date类型属性值
*/
Date getDate(K key);
/*-------------------------- 基本类型 end -------------------------------*/
}

View File

@ -1,103 +1,296 @@
package cn.hutool.core.lang.getter;
import cn.hutool.core.convert.Convert;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* 基于分组的Get接口
* @author Looly
*
* @param <K> 键类型
* @param <G> 分组键类型
* @author Looly
* @since 6.0.0
*/
public interface GroupedTypeGetter {
/*-------------------------- 基本类型 start -------------------------------*/
public interface GroupedTypeGetter<K, G> {
/**
* 获取字符串型属性值<br>
* 获取Object属性值最原始的对象获取没有任何转换或类型判断
*
* @param key 属性名
* @param key 属性名
* @param group 分组
* @param defaultValue 默认值
* @return 属性值
*/
Object getObjByGroup(K key, G group, Object defaultValue);
/**
* 获取Object属性值最原始的对象获取没有任何转换或类型判断
*
* @param key 属性名
* @param group 分组
* @return 属性值
*/
String getStrByGroup(String key, String group);
default Object getObjByGroup(final K key, final G group) {
return getObjByGroup(key, group, null);
}
/**
* 获取指定类型的值默认自动转换值类型
*
* @param <T> 目标类型
* @param key
* @param group 分组
* @param type 目标类型
* @return 结果值
*/
default <T> T getByGroup(final K key, final G group, final Type type) {
return getByGroup(key, group, type, null);
}
/**
* 获取指定类型的值默认自动转换值类型
*
* @param <T> 目标类型
* @param key
* @param group 分组
* @param type 目标类型
* @param defaultValue 默认值
* @return 结果值
*/
default <T> T getByGroup(final K key, final G group, final Type type, final T defaultValue) {
return Convert.convert(type, getObjByGroup(key, group), defaultValue);
}
/**
* 获取字符串型属性值
*
* @param key 属性名
* @param group 分组
* @param defaultValue 默认值
* @return 属性值
*/
default String getStrByGroup(final K key, final G group, final String defaultValue) {
return getByGroup(key, group, String.class, defaultValue);
}
/**
* 获取字符串型属性值
*
* @param key 属性名
* @param group 分组
* @return 属性值
*/
default String getStrByGroup(final K key, final G group) {
return getStrByGroup(key, group, null);
}
/**
* 获取int型属性值<br>
*
* @param key 属性名
* @param key 属性名
* @param group 分组
* @param defaultValue 默认值
* @return 属性值
*/
default Integer getIntByGroup(final K key, final G group, final Integer defaultValue) {
return getByGroup(key, group, Integer.class, defaultValue);
}
/**
* 获取int型属性值<br>
*
* @param key 属性名
* @param group 分组
* @return 属性值
*/
Integer getIntByGroup(String key, String group);
default Integer getIntByGroup(final K key, final G group) {
return getIntByGroup(key, group, null);
}
/**
* 获取short型属性值<br>
*
* @param key 属性名
* @param key 属性名
* @param group 分组
* @param defaultValue 默认值
* @return 属性值
*/
default Short getShortByGroup(final K key, final G group, final Short defaultValue) {
return getByGroup(key, group, Short.class, defaultValue);
}
/**
* 获取short型属性值<br>
*
* @param key 属性名
* @param group 分组
* @return 属性值
*/
Short getShortByGroup(String key, String group);
default Short getShortByGroup(final K key, final G group) {
return getShortByGroup(key, group, null);
}
/**
* 获取boolean型属性值<br>
*
* @param key 属性名
* @param key 属性名
* @param group 分组
* @param defaultValue 默认值
* @return 属性值
*/
default Boolean getBoolByGroup(final K key, final G group, final Boolean defaultValue) {
return getByGroup(key, group, Boolean.class, defaultValue);
}
/**
* 获取boolean型属性值<br>
*
* @param key 属性名
* @param group 分组
* @return 属性值
*/
Boolean getBoolByGroup(String key, String group);
default Boolean getBoolByGroup(final K key, final G group) {
return getBoolByGroup(key, group, null);
}
/**
* 获取Long型属性值<br>
*
* @param key 属性名
* @param key 属性名
* @param group 分组
* @param defaultValue 默认值
* @return 属性值
*/
default Long getLongByGroup(final K key, final G group, final Long defaultValue) {
return getByGroup(key, group, Long.class, defaultValue);
}
/**
* 获取Long型属性值<br>
*
* @param key 属性名
* @param group 分组
* @return 属性值
*/
Long getLongByGroup(String key, String group);
default Long getLongByGroup(final K key, final G group) {
return getLongByGroup(key, group, null);
}
/**
* 获取char型属性值<br>
*
* @param key 属性名
* @param key 属性名
* @param group 分组
* @param defaultValue 默认值
* @return 属性值
*/
default Character getCharByGroup(final K key, final G group, final Character defaultValue) {
return getByGroup(key, group, Character.class, defaultValue);
}
/**
* 获取char型属性值<br>
*
* @param key 属性名
* @param group 分组
* @return 属性值
*/
Character getCharByGroup(String key, String group);
default Character getCharByGroup(final K key, final G group) {
return getCharByGroup(key, group, null);
}
/**
* 获取double型属性值<br>
*
* @param key 属性名
* @param key 属性名
* @param group 分组
* @param defaultValue 默认值
* @return 属性值
*/
default Double getDoubleByGroup(final K key, final G group, final Double defaultValue) {
return getByGroup(key, group, Double.class, defaultValue);
}
/**
* 获取double型属性值<br>
*
* @param key 属性名
* @param group 分组
* @return 属性值
*/
Double getDoubleByGroup(String key, String group);
default Double getDoubleByGroup(final K key, final G group) {
return getDoubleByGroup(key, group, null);
}
/**
* 获取byte型属性值<br>
*
* @param key 属性名
* @param key 属性名
* @param group 分组
* @param defaultValue 默认值
* @return 属性值
*/
default Byte getByteByGroup(final K key, final G group, final Byte defaultValue) {
return getByGroup(key, group, Byte.class, defaultValue);
}
/**
* 获取byte型属性值<br>
*
* @param key 属性名
* @param group 分组
* @return 属性值
*/
Byte getByteByGroup(String key, String group);
default Byte getByteByGroup(final K key, final G group) {
return getByteByGroup(key, group, null);
}
/**
* 获取BigDecimal型属性值<br>
*
* @param key 属性名
* @param key 属性名
* @param group 分组
* @param defaultValue 默认值
* @return 属性值
*/
default BigDecimal getBigDecimalByGroup(final K key, final G group, final BigDecimal defaultValue) {
return getByGroup(key, group, BigDecimal.class, defaultValue);
}
/**
* 获取BigDecimal型属性值<br>
*
* @param key 属性名
* @param group 分组
* @return 属性值
*/
BigDecimal getBigDecimalByGroup(String key, String group);
default BigDecimal getBigDecimalByGroup(final K key, final G group) {
return getBigDecimalByGroup(key, group, null);
}
/**
* 获取BigInteger型属性值<br>
*
* @param key 属性名
* @param key 属性名
* @param group 分组
* @param defaultValue 默认值
* @return 属性值
*/
default BigInteger getBigIntegerByGroup(final K key, final G group, final BigInteger defaultValue) {
return getByGroup(key, group, BigInteger.class, defaultValue);
}
/**
* 获取BigInteger型属性值<br>
*
* @param key 属性名
* @param group 分组
* @return 属性值
*/
BigInteger getBigIntegerByGroup(String key, String group);
/*-------------------------- 基本类型 end -------------------------------*/
default BigInteger getBigIntegerByGroup(final K key, final G group) {
return getBigIntegerByGroup(key, group, null);
}
}

View File

@ -1,102 +0,0 @@
package cn.hutool.core.lang.getter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
/**
* 列表类型的Get接口
* @author Looly
*
*/
public interface ListTypeGetter {
/*-------------------------- List类型 start -------------------------------*/
/**
* 获取Object型属性值列表
*
* @param key 属性名
* @return 属性值列表
*/
List<Object> getObjList(String key);
/**
* 获取String型属性值列表
*
* @param key 属性名
* @return 属性值列表
*/
List<String> getStrList(String key);
/**
* 获取Integer型属性值列表
*
* @param key 属性名
* @return 属性值列表
*/
List<Integer> getIntList(String key);
/**
* 获取Short型属性值列表
*
* @param key 属性名
* @return 属性值列表
*/
List<Short> getShortList(String key);
/**
* 获取Boolean型属性值列表
*
* @param key 属性名
* @return 属性值列表
*/
List<Boolean> getBoolList(String key);
/**
* 获取Long型属性值列表
*
* @param key 属性名
* @return 属性值列表
*/
List<Long> getLongList(String key);
/**
* 获取Character型属性值列表
*
* @param key 属性名
* @return 属性值列表
*/
List<Character> getCharList(String key);
/**
* 获取Double型属性值列表
*
* @param key 属性名
* @return 属性值列表
*/
List<Double> getDoubleList(String key);
/**
* 获取Byte型属性值列表
*
* @param key 属性名
* @return 属性值列表
*/
List<Byte> getByteList(String key);
/**
* 获取BigDecimal型属性值列表
*
* @param key 属性名
* @return 属性值列表
*/
List<BigDecimal> getBigDecimalList(String key);
/**
* 获取BigInteger型属性值列表
*
* @param key 属性名
* @return 属性值列表
*/
List<BigInteger> getBigIntegerList(String key);
/*-------------------------- List类型 end -------------------------------*/
}

View File

@ -1,117 +0,0 @@
package cn.hutool.core.lang.getter;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* 可选默认值的数组类型的Get接口
* 提供一个统一的接口定义返回不同类型的值基本类型<br>
* 如果值不存在或获取错误返回默认值
*
* @author Looly
* @since 4.0.2
*
*/
public interface OptArrayTypeGetter {
/*-------------------------- 数组类型 start -------------------------------*/
/**
* 获取Object型属性值数组
*
* @param key 属性名
* @param defaultValue 默认数组值
* @return 属性值列表
*/
Object[] getObjs(String key, Object[] defaultValue);
/**
* 获取String型属性值数组
*
* @param key 属性名
* @param defaultValue 默认数组值
* @return 属性值列表
*/
String[] getStrs(String key, String[] defaultValue);
/**
* 获取Integer型属性值数组
*
* @param key 属性名
* @param defaultValue 默认数组值
* @return 属性值列表
*/
Integer[] getInts(String key, Integer[] defaultValue);
/**
* 获取Short型属性值数组
*
* @param key 属性名
* @param defaultValue 默认数组值
* @return 属性值列表
*/
Short[] getShorts(String key, Short[] defaultValue);
/**
* 获取Boolean型属性值数组
*
* @param key 属性名
* @param defaultValue 默认数组值
* @return 属性值列表
*/
Boolean[] getBools(String key, Boolean[] defaultValue);
/**
* 获取Long型属性值数组
*
* @param key 属性名
* @param defaultValue 默认数组值
* @return 属性值列表
*/
Long[] getLongs(String key, Long[] defaultValue);
/**
* 获取Character型属性值数组
*
* @param key 属性名
* @param defaultValue 默认数组值
* @return 属性值列表
*/
Character[] getChars(String key, Character[] defaultValue);
/**
* 获取Double型属性值数组
*
* @param key 属性名
* @param defaultValue 默认数组值
* @return 属性值列表
*/
Double[] getDoubles(String key, Double[] defaultValue);
/**
* 获取Byte型属性值数组
*
* @param key 属性名
* @param defaultValue 默认数组值
* @return 属性值列表
*/
Byte[] getBytes(String key, Byte[] defaultValue);
/**
* 获取BigInteger型属性值数组
*
* @param key 属性名
* @param defaultValue 默认数组值
* @return 属性值列表
*/
BigInteger[] getBigIntegers(String key, BigInteger[] defaultValue);
/**
* 获取BigDecimal型属性值数组
*
* @param key 属性名
* @param defaultValue 默认数组值
* @return 属性值列表
*/
BigDecimal[] getBigDecimals(String key, BigDecimal[] defaultValue);
/*-------------------------- 数组类型 end -------------------------------*/
}

View File

@ -1,153 +0,0 @@
package cn.hutool.core.lang.getter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
/**
* 可选默认值的基本类型的getter接口<br>
* 提供一个统一的接口定义返回不同类型的值基本类型<br>
* 如果值不存在或获取错误返回默认值
* @author Looly
*/
public interface OptBasicTypeGetter<K> {
/*-------------------------- 基本类型 start -------------------------------*/
/**
* 获取Object属性值
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
Object getObj(K key, Object defaultValue);
/**
* 获取字符串型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
String getStr(K key, String defaultValue);
/**
* 获取int型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
Integer getInt(K key, Integer defaultValue);
/**
* 获取short型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
Short getShort(K key, Short defaultValue);
/**
* 获取boolean型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
Boolean getBool(K key, Boolean defaultValue);
/**
* 获取Long型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
Long getLong(K key, Long defaultValue);
/**
* 获取char型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
Character getChar(K key, Character defaultValue);
/**
* 获取float型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
Float getFloat(K key, Float defaultValue);
/**
* 获取double型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
Double getDouble(K key, Double defaultValue);
/**
* 获取byte型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
Byte getByte(K key, Byte defaultValue);
/**
* 获取BigDecimal型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
BigDecimal getBigDecimal(K key, BigDecimal defaultValue);
/**
* 获取BigInteger型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
BigInteger getBigInteger(K key, BigInteger defaultValue);
/**
* 获得Enum类型的值
*
* @param <E> 枚举类型
* @param clazz Enum的Class
* @param key KEY
* @param defaultValue 默认值
* @return Enum类型的值无则返回Null
*/
<E extends Enum<E>> E getEnum(Class<E> clazz, K key, E defaultValue);
/**
* 获取Date类型值
* @param key 属性名
* @param defaultValue 默认值
* @return Date类型属性值
*/
Date getDate(K key, Date defaultValue);
/*-------------------------- 基本类型 end -------------------------------*/
}

View File

@ -1,133 +0,0 @@
package cn.hutool.core.lang.getter;
import cn.hutool.core.convert.Convert;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
/**
* 基本类型的getter接口抽象实现所有类型的值获取都是通过将getObj获得的值转换而来<br>
* 用户只需实现getObj方法即可其他类型将会从Object结果中转换
* 在不提供默认值的情况下 如果值不存在或获取错误返回null<br>
*
* @author Looly
*/
public interface OptNullBasicTypeFromObjectGetter<K> extends OptNullBasicTypeGetter<K> {
@Override
default String getStr(final K key, final String defaultValue) {
final Object obj = getObj(key);
if (null == obj) {
return defaultValue;
}
return Convert.toStr(obj, defaultValue);
}
@Override
default Integer getInt(final K key, final Integer defaultValue) {
final Object obj = getObj(key);
if (null == obj) {
return defaultValue;
}
return Convert.toInt(obj, defaultValue);
}
@Override
default Short getShort(final K key, final Short defaultValue) {
final Object obj = getObj(key);
if (null == obj) {
return defaultValue;
}
return Convert.toShort(obj, defaultValue);
}
@Override
default Boolean getBool(final K key, final Boolean defaultValue) {
final Object obj = getObj(key);
if (null == obj) {
return defaultValue;
}
return Convert.toBool(obj, defaultValue);
}
@Override
default Long getLong(final K key, final Long defaultValue) {
final Object obj = getObj(key);
if (null == obj) {
return defaultValue;
}
return Convert.toLong(obj, defaultValue);
}
@Override
default Character getChar(final K key, final Character defaultValue) {
final Object obj = getObj(key);
if (null == obj) {
return defaultValue;
}
return Convert.toChar(obj, defaultValue);
}
@Override
default Float getFloat(final K key, final Float defaultValue) {
final Object obj = getObj(key);
if (null == obj) {
return defaultValue;
}
return Convert.toFloat(obj, defaultValue);
}
@Override
default Double getDouble(final K key, final Double defaultValue) {
final Object obj = getObj(key);
if (null == obj) {
return defaultValue;
}
return Convert.toDouble(obj, defaultValue);
}
@Override
default Byte getByte(final K key, final Byte defaultValue) {
final Object obj = getObj(key);
if (null == obj) {
return defaultValue;
}
return Convert.toByte(obj, defaultValue);
}
@Override
default BigDecimal getBigDecimal(final K key, final BigDecimal defaultValue) {
final Object obj = getObj(key);
if (null == obj) {
return defaultValue;
}
return Convert.toBigDecimal(obj, defaultValue);
}
@Override
default BigInteger getBigInteger(final K key, final BigInteger defaultValue) {
final Object obj = getObj(key);
if (null == obj) {
return defaultValue;
}
return Convert.toBigInteger(obj, defaultValue);
}
@Override
default <E extends Enum<E>> E getEnum(final Class<E> clazz, final K key, final E defaultValue) {
final Object obj = getObj(key);
if (null == obj) {
return defaultValue;
}
return Convert.toEnum(clazz, obj, defaultValue);
}
@Override
default Date getDate(final K key, final Date defaultValue) {
final Object obj = getObj(key);
if (null == obj) {
return defaultValue;
}
return Convert.toDate(obj, defaultValue);
}
}

View File

@ -1,80 +0,0 @@
package cn.hutool.core.lang.getter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import cn.hutool.core.convert.Convert;
/**
* 基本类型的getter接口抽象实现所有类型的值获取都是通过将String转换而来<br>
* 用户只需实现getStr方法即可其他类型将会从String结果中转换 在不提供默认值的情况下 如果值不存在或获取错误返回null<br>
*
* @author Looly
*/
public interface OptNullBasicTypeFromStringGetter<K> extends OptNullBasicTypeGetter<K> {
@Override
default Object getObj(final K key, final Object defaultValue) {
return getStr(key, null == defaultValue ? null : defaultValue.toString());
}
@Override
default Integer getInt(final K key, final Integer defaultValue) {
return Convert.toInt(getStr(key), defaultValue);
}
@Override
default Short getShort(final K key, final Short defaultValue) {
return Convert.toShort(getStr(key), defaultValue);
}
@Override
default Boolean getBool(final K key, final Boolean defaultValue) {
return Convert.toBool(getStr(key), defaultValue);
}
@Override
default Long getLong(final K key, final Long defaultValue) {
return Convert.toLong(getStr(key), defaultValue);
}
@Override
default Character getChar(final K key, final Character defaultValue) {
return Convert.toChar(getStr(key), defaultValue);
}
@Override
default Float getFloat(final K key, final Float defaultValue) {
return Convert.toFloat(getStr(key), defaultValue);
}
@Override
default Double getDouble(final K key, final Double defaultValue) {
return Convert.toDouble(getStr(key), defaultValue);
}
@Override
default Byte getByte(final K key, final Byte defaultValue) {
return Convert.toByte(getStr(key), defaultValue);
}
@Override
default BigDecimal getBigDecimal(final K key, final BigDecimal defaultValue) {
return Convert.toBigDecimal(getStr(key), defaultValue);
}
@Override
default BigInteger getBigInteger(final K key, final BigInteger defaultValue) {
return Convert.toBigInteger(getStr(key), defaultValue);
}
@Override
default <E extends Enum<E>> E getEnum(final Class<E> clazz, final K key, final E defaultValue) {
return Convert.toEnum(clazz, getStr(key), defaultValue);
}
@Override
default Date getDate(final K key, final Date defaultValue) {
return Convert.toDate(getStr(key), defaultValue);
}
}

View File

@ -1,176 +0,0 @@
package cn.hutool.core.lang.getter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
/**
* 基本类型的getter接口抽象实现<br>
* 提供一个统一的接口定义返回不同类型的值基本类型<br>
* 在不提供默认值的情况下 如果值不存在或获取错误返回null<br>
* 用户只需实现{@link OptBasicTypeGetter}接口即可
* @author Looly
*/
public interface OptNullBasicTypeGetter<K> extends BasicTypeGetter<K>, OptBasicTypeGetter<K>{
@Override
default Object getObj(final K key) {
return getObj(key, null);
}
/**
* 获取字符串型属性值<br>
* 无值或获取错误返回null
*
* @param key 属性名
* @return 属性值
*/
@Override
default String getStr(final K key){
return this.getStr(key, null);
}
/**
* 获取int型属性值<br>
* 无值或获取错误返回null
*
* @param key 属性名
* @return 属性值
*/
@Override
default Integer getInt(final K key) {
return this.getInt(key, null);
}
/**
* 获取short型属性值<br>
* 无值或获取错误返回null
*
* @param key 属性名
* @return 属性值
*/
@Override
default Short getShort(final K key){
return this.getShort(key, null);
}
/**
* 获取boolean型属性值<br>
* 无值或获取错误返回null
*
* @param key 属性名
* @return 属性值
*/
@Override
default Boolean getBool(final K key){
return this.getBool(key, null);
}
/**
* 获取long型属性值<br>
* 无值或获取错误返回null
*
* @param key 属性名
* @return 属性值
*/
@Override
default Long getLong(final K key){
return this.getLong(key, null);
}
/**
* 获取char型属性值<br>
* 无值或获取错误返回null
*
* @param key 属性名
* @return 属性值
*/
@Override
default Character getChar(final K key){
return this.getChar(key, null);
}
/**
* 获取float型属性值<br>
* 无值或获取错误返回null
*
* @param key 属性名
* @return 属性值
*/
@Override
default Float getFloat(final K key){
return this.getFloat(key, null);
}
/**
* 获取double型属性值<br>
* 无值或获取错误返回null
*
* @param key 属性名
* @return 属性值
*/
@Override
default Double getDouble(final K key){
return this.getDouble(key, null);
}
/**
* 获取byte型属性值<br>
* 无值或获取错误返回null
*
* @param key 属性名
* @return 属性值
*/
@Override
default Byte getByte(final K key){
return this.getByte(key, null);
}
/**
* 获取BigDecimal型属性值<br>
* 无值或获取错误返回null
*
* @param key 属性名
* @return 属性值
*/
@Override
default BigDecimal getBigDecimal(final K key){
return this.getBigDecimal(key, null);
}
/**
* 获取BigInteger型属性值<br>
* 无值或获取错误返回null
*
* @param key 属性名
* @return 属性值
*/
@Override
default BigInteger getBigInteger(final K key){
return this.getBigInteger(key, null);
}
/**
* 获取Enum型属性值<br>
* 无值或获取错误返回null
*
* @param clazz Enum Class
* @param key 属性名
* @return 属性值
*/
@Override
default <E extends Enum<E>> E getEnum(final Class<E> clazz, final K key) {
return this.getEnum(clazz, key, null);
}
/**
* 获取Date型属性值<br>
* 无值或获取错误返回null
*
* @param key 属性名
* @return 属性值
*/
@Override
default Date getDate(final K key) {
return this.getDate(key, null);
}
}

View File

@ -0,0 +1,545 @@
package cn.hutool.core.lang.getter;
import cn.hutool.core.convert.CompositeConverter;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.convert.Converter;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
/**
* 基本类型的getter接口<br>
* 提供一个统一的接口定义返回不同类型的值基本类型定义类型包括
* <ul>
* <li>Object</li>
* <li>String</li>
* <li>Integer</li>
* <li>Short</li>
* <li>Boolean</li>
* <li>Long</li>
* <li>Character</li>
* <li>Float</li>
* <li>Double</li>
* <li>Byte</li>
* <li>BigDecimal</li>
* <li>BigInteger</li>
* <li>Enum</li>
* <li>Number</li>
* <li>Date</li>
* <li>java.sql.Time</li>
* <li>java.sql.Timestamp</li>
* <li>java.sql.Timestamp</li>
* <li>LocalDateTime</li>
* <li>LocalDate</li>
* <li>LocalTime</li>
* </ul>
* 通过实现此接口最简单方式为通过实现{@link #getObj(Object, Object)}方法完成所有类型的值获取获取默认采用
* {@link Convert}方式自动转换如果有自定义实现重写对应getXXX方法即可
*
* @param <K> 键类型
* @author Looly
* @since 6.0.0
*/
public interface TypeGetter<K> {
/*-------------------------- 基本类型 start -------------------------------*/
/**
* 获取Object属性值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
Object getObj(K key, Object defaultValue);
/**
* 获取Object属性值最原始的对象获取没有任何转换或类型判断
*
* @param key 属性名
* @return 属性值
*/
default Object getObj(final K key) {
return getObj(key, null);
}
/**
* 获取指定类型的值默认自动转换值类型
*
* @param <T> 目标类型
* @param key
* @param type 目标类型
* @return 结果值
*/
default <T> T get(final K key, final Type type) {
return get(key, type, null);
}
/**
* 获取指定类型的值默认自动转换值类型
*
* @param <T> 目标类型
* @param key
* @param type 目标类型
* @param defaultValue 默认值
* @return 结果值
*/
default <T> T get(final K key, final Type type, final T defaultValue) {
return get(key, type, CompositeConverter.getInstance(), defaultValue);
}
/**
* 获取指定类型的值默认自动转换值类型
*
* @param <T> 目标类型
* @param key
* @param type 目标类型
* @param converter 自定义转换器
* @param defaultValue 默认值
* @return 结果值
*/
default <T> T get(final K key, final Type type, final Converter converter, final T defaultValue) {
return converter.convert(type, getObj(key), defaultValue);
}
/**
* 获取字符串型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
default String getStr(final K key, final String defaultValue) {
return get(key, String.class, defaultValue);
}
/**
* 获取字符串型属性值
*
* @param key 属性名
* @return 属性值
*/
default String getStr(final K key) {
return getStr(key, null);
}
/**
* 获取int型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
default Integer getInt(final K key, final Integer defaultValue) {
return get(key, Integer.class, defaultValue);
}
/**
* 获取int型属性值
*
* @param key 属性名
* @return 属性值
*/
default Integer getInt(final K key) {
return getInt(key, null);
}
/**
* 获取short型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
default Short getShort(final K key, final Short defaultValue) {
return get(key, Short.class, defaultValue);
}
/**
* 获取short型属性值
*
* @param key 属性名
* @return 属性值
*/
default Short getShort(final K key) {
return getShort(key, null);
}
/**
* 获取boolean型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
default Boolean getBool(final K key, final Boolean defaultValue) {
return get(key, Boolean.class, defaultValue);
}
/**
* 获取boolean型属性值
*
* @param key 属性名
* @return 属性值
*/
default Boolean getBool(final K key) {
return getBool(key, null);
}
/**
* 获取Long型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
default Long getLong(final K key, final Long defaultValue) {
return get(key, Long.class, defaultValue);
}
/**
* 获取long型属性值
*
* @param key 属性名
* @return 属性值
*/
default Long getLong(final K key) {
return getLong(key, null);
}
/**
* 获取char型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
default Character getChar(final K key, final Character defaultValue) {
return get(key, Character.class, defaultValue);
}
/**
* 获取char型属性值
*
* @param key 属性名
* @return 属性值
*/
default Character getChar(final K key) {
return getChar(key, null);
}
/**
* 获取float型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
default Float getFloat(final K key, final Float defaultValue) {
return get(key, Float.class, defaultValue);
}
/**
* 获取float型属性值<br>
*
* @param key 属性名
* @return 属性值
*/
default Float getFloat(final K key) {
return getFloat(key, null);
}
/**
* 获取double型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
default Double getDouble(final K key, final Double defaultValue) {
return get(key, Double.class, defaultValue);
}
/**
* 获取double型属性值
*
* @param key 属性名
* @return 属性值
*/
default Double getDouble(final K key) {
return getDouble(key, null);
}
/**
* 获取byte型属性值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
default Byte getByte(final K key, final Byte defaultValue) {
return get(key, Byte.class, defaultValue);
}
/**
* 获取byte型属性值
*
* @param key 属性名
* @return 属性值
*/
default Byte getByte(final K key) {
return getByte(key, null);
}
/**
* 获取bytes型属性值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
default byte[] getBytes(final K key, final byte[] defaultValue) {
return get(key, byte[].class, defaultValue);
}
/**
* 获取bytes型属性值
*
* @param key 属性名
* @return 属性值
*/
default byte[] getBytes(final K key) {
return getBytes(key, null);
}
/**
* 获取BigDecimal型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
default BigDecimal getBigDecimal(final K key, final BigDecimal defaultValue) {
return get(key, BigDecimal.class, defaultValue);
}
/**
* 获取BigDecimal型属性值
*
* @param key 属性名
* @return 属性值
*/
default BigDecimal getBigDecimal(final K key) {
return getBigDecimal(key, null);
}
/**
* 获取BigInteger型属性值<br>
* 若获得的值为不可见字符使用默认值
*
* @param key 属性名
* @param defaultValue 默认值
* @return 属性值无对应值返回defaultValue
*/
default BigInteger getBigInteger(final K key, final BigInteger defaultValue) {
return get(key, BigInteger.class, defaultValue);
}
/**
* 获取BigInteger型属性值
*
* @param key 属性名
* @return 属性值
*/
default BigInteger getBigInteger(final K key) {
return getBigInteger(key, null);
}
/**
* 获得Enum类型的值
*
* @param <E> 枚举类型
* @param clazz Enum的Class
* @param key KEY
* @param defaultValue 默认值
* @return Enum类型的值无则返回Null
*/
default <E extends Enum<E>> E getEnum(final Class<E> clazz, final K key, final E defaultValue) {
return get(key, clazz, defaultValue);
}
/**
* 获得Enum类型的值
*
* @param <E> 枚举类型
* @param clazz Enum的Class
* @param key KEY
* @return Enum类型的值无则返回Null
*/
default <E extends Enum<E>> E getEnum(final Class<E> clazz, final K key) {
return getEnum(clazz, key, null);
}
/**
* 获取Number类型值
*
* @param key 属性名
* @param defaultValue 默认值
* @return Number类型属性值
*/
default Number getNumber(final K key, final Number defaultValue) {
return get(key, Number.class, defaultValue);
}
/**
* 获取Number类型值
*
* @param key 属性名
* @return Number类型属性值
*/
default Number getNumber(final K key) {
return getNumber(key, null);
}
/**
* 获取Date类型值
*
* @param key 属性名
* @param defaultValue 默认值
* @return Date类型属性值
*/
default Date getDate(final K key, final Date defaultValue) {
return get(key, Date.class, defaultValue);
}
/**
* 获取Date类型值
*
* @param key 属性名
* @return Date类型属性值
*/
default Date getDate(final K key) {
return getDate(key, null);
}
/**
* 获取LocalTime类型值
*
* @param key 属性名
* @param defaultValue 默认值
* @return LocalTime类型属性值
*/
default Time getSqlTime(final K key, final Time defaultValue) {
return get(key, Time.class, defaultValue);
}
/**
* 获取Time类型值
*
* @param key 属性名
* @return Time类型属性值
*/
default Time getSqlTime(final K key) {
return getSqlTime(key, null);
}
/**
* 获取Timestamp类型值
*
* @param key 属性名
* @param defaultValue 默认值
* @return Timestamp类型属性值
*/
default Timestamp getSqlTimestamp(final K key, final Timestamp defaultValue) {
return get(key, Timestamp.class, defaultValue);
}
/**
* 获取Timestamp类型值
*
* @param key 属性名
* @return Timestamp类型属性值
*/
default Timestamp getSqlTimestamp(final K key) {
return getSqlTimestamp(key, null);
}
/**
* 获取LocalDateTime类型值
*
* @param key 属性名
* @param defaultValue 默认值
* @return LocalDateTime类型属性值
*/
default LocalDateTime getLocalDateTime(final K key, final LocalDateTime defaultValue) {
return get(key, LocalDateTime.class, defaultValue);
}
/**
* 获取LocalDateTime类型值
*
* @param key 属性名
* @return LocalDateTime类型属性值
*/
default LocalDateTime getLocalDateTime(final K key) {
return getLocalDateTime(key, null);
}
/**
* 获取LocalDate类型值
*
* @param key 属性名
* @param defaultValue 默认值
* @return LocalTime类型属性值
*/
default LocalDate getLocalDate(final K key, final LocalDate defaultValue) {
return get(key, LocalDate.class, defaultValue);
}
/**
* 获取LocalDate类型值
*
* @param key 属性名
* @return LocalTime类型属性值
*/
default LocalDate getLocalDate(final K key) {
return getLocalDate(key, null);
}
/**
* 获取LocalTime类型值
*
* @param key 属性名
* @param defaultValue 默认值
* @return LocalTime类型属性值
*/
default LocalTime getLocalTime(final K key, final LocalTime defaultValue) {
return get(key, LocalTime.class, defaultValue);
}
/**
* 获取LocalTime类型值
*
* @param key 属性名
* @return LocalTime类型属性值
*/
default LocalTime getLocalTime(final K key) {
return getLocalTime(key, null);
}
/*-------------------------- 基本类型 end -------------------------------*/
}

View File

@ -1,19 +0,0 @@
package cn.hutool.core.lang.hash;
/**
* Hash计算接口
*
* @param <T> 被计算hash的对象类型
* @author looly
* @since 5.7.15
*/
@FunctionalInterface
public interface Hash<T> {
/**
* 计算Hash值
*
* @param t 对象
* @return hash
*/
Number hash(T t);
}

View File

@ -3,7 +3,7 @@ package cn.hutool.core.lang.mutable;
import cn.hutool.core.comparator.CompareUtil;
/**
* 可变 <code>int</code> 类型
* 可变 {@code int} 类型
*
* @see Integer
* @since 3.0.1

View File

@ -7,17 +7,14 @@ import cn.hutool.core.collection.SetUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.CloneRuntimeException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.func.LambdaInfo;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.lang.func.SerFunction;
import cn.hutool.core.lang.func.SerSupplier;
import cn.hutool.core.lang.getter.BasicTypeGetter;
import cn.hutool.core.lang.getter.TypeGetter;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@ -29,7 +26,7 @@ import java.util.Objects;
*
* @author looly
*/
public class Dict extends CustomKeyMap<String, Object> implements BasicTypeGetter<String> {
public class Dict extends CustomKeyMap<String, Object> implements TypeGetter<String> {
private static final long serialVersionUID = 6135423866861206530L;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
@ -352,10 +349,22 @@ public class Dict extends CustomKeyMap<String, Object> implements BasicTypeGette
// -------------------------------------------------------------------- Set end
// -------------------------------------------------------------------- Get start
@Override
public Object getObj(final String key) {
return super.get(key);
public Object getObj(final String key, final Object defaultValue) {
return getOrDefault(key, defaultValue);
}
/**
* 根据lambda的方法引用获取
*
* @param func 方法引用
* @param <P> 参数类型
* @param <T> 返回值类型
* @return 获取表达式对应属性和返回的对象
*/
public <P, T> T get(final SerFunction<P, T> func) {
final LambdaInfo lambdaInfo = LambdaUtil.resolve(func);
return get(lambdaInfo.getFieldName(), lambdaInfo.getReturnType());
}
/**
@ -366,151 +375,9 @@ public class Dict extends CustomKeyMap<String, Object> implements BasicTypeGette
* @return 字段值
* @since 4.6.3
*/
public <T> T getBean(final String attr) {
return get(attr, null);
}
/**
* 获得特定类型值
*
* @param <T> 值类型
* @param attr 字段名
* @param defaultValue 默认值
* @return 字段值
*/
@SuppressWarnings("unchecked")
public <T> T get(final String attr, final T defaultValue) {
final Object result = get(attr);
return (T) (result != null ? result : defaultValue);
}
/**
* @param attr 字段名
* @return 字段值
*/
@Override
public String getStr(final String attr) {
return Convert.toStr(get(attr), null);
}
/**
* @param attr 字段名
* @return 字段值
*/
@Override
public Integer getInt(final String attr) {
return Convert.toInt(get(attr), null);
}
/**
* @param attr 字段名
* @return 字段值
*/
@Override
public Long getLong(final String attr) {
return Convert.toLong(get(attr), null);
}
/**
* @param attr 字段名
* @return 字段值
*/
@Override
public Float getFloat(final String attr) {
return Convert.toFloat(get(attr), null);
}
@Override
public Short getShort(final String attr) {
return Convert.toShort(get(attr), null);
}
@Override
public Character getChar(final String attr) {
return Convert.toChar(get(attr), null);
}
@Override
public Double getDouble(final String attr) {
return Convert.toDouble(get(attr), null);
}
@Override
public Byte getByte(final String attr) {
return Convert.toByte(get(attr), null);
}
/**
* @param attr 字段名
* @return 字段值
*/
@Override
public Boolean getBool(final String attr) {
return Convert.toBool(get(attr), null);
}
/**
* @param attr 字段名
* @return 字段值
*/
@Override
public BigDecimal getBigDecimal(final String attr) {
return Convert.toBigDecimal(get(attr));
}
/**
* @param attr 字段名
* @return 字段值
*/
@Override
public BigInteger getBigInteger(final String attr) {
return Convert.toBigInteger(get(attr));
}
@Override
public <E extends Enum<E>> E getEnum(final Class<E> clazz, final String key) {
return Convert.toEnum(clazz, get(key));
}
/**
* @param attr 字段名
* @return 字段值
*/
public byte[] getBytes(final String attr) {
return get(attr, null);
}
/**
* @param attr 字段名
* @return 字段值
*/
@Override
public Date getDate(final String attr) {
return get(attr, null);
}
/**
* @param attr 字段名
* @return 字段值
*/
public Time getTime(final String attr) {
return get(attr, null);
}
/**
* @param attr 字段名
* @return 字段值
*/
public Timestamp getTimestamp(final String attr) {
return get(attr, null);
}
/**
* @param attr 字段名
* @return 字段值
*/
public Number getNumber(final String attr) {
return get(attr, null);
public <T> T getBean(final String attr) {
return (T) get(attr);
}
/**

View File

@ -3,6 +3,7 @@ package cn.hutool.core.map;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.func.Wrapper;
import cn.hutool.core.reflect.ClassUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ObjUtil;
@ -33,6 +34,7 @@ import java.util.stream.Collectors;
* 也可以在获取到{@link TreeEntry}使用节点本身的方法对数进行操作或访问
*
* @param <K> key类型
* @param <V> 值类型
* @author huangchengxing
*/
public class LinkedForestMap<K, V> implements ForestMap<K, V> {
@ -48,11 +50,11 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
private final boolean allowOverrideParent;
/**
* 构建{@link LinkedForestMap}
* 构建{@code LinkedForestMap}
*
* @param allowOverrideParent 当指定节点已经与其他节点构成了父子关系是否允许将该节点的父节点强制替换为指定节点
*/
public LinkedForestMap(boolean allowOverrideParent) {
public LinkedForestMap(final boolean allowOverrideParent) {
this.allowOverrideParent = allowOverrideParent;
this.nodes = new LinkedHashMap<>();
}
@ -86,7 +88,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @return 是否
*/
@Override
public boolean containsKey(Object key) {
public boolean containsKey(final Object key) {
return nodes.containsKey(key);
}
@ -97,7 +99,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @return 是否
*/
@Override
public boolean containsValue(Object value) {
public boolean containsValue(final Object value) {
return nodes.containsValue(value);
}
@ -108,7 +110,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @return 节点
*/
@Override
public TreeEntry<K, V> get(Object key) {
public TreeEntry<K, V> get(final Object key) {
return nodes.get(key);
}
@ -126,7 +128,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @return 删除的且引用关系已经改变的节点若key没有对应节点则返回null
*/
@Override
public TreeEntry<K, V> remove(Object key) {
public TreeEntry<K, V> remove(final Object key) {
final TreeEntryNode<K, V> target = nodes.remove(key);
if (ObjUtil.isNull(target)) {
return null;
@ -189,7 +191,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
/**
* {@link TreeEntryNode}包装为{@link EntryNodeWrapper}
*/
private Map.Entry<K, TreeEntry<K, V>> wrap(Map.Entry<K, TreeEntryNode<K, V>> nodeEntry) {
private Map.Entry<K, TreeEntry<K, V>> wrap(final Map.Entry<K, TreeEntryNode<K, V>> nodeEntry) {
return new EntryNodeWrapper<>(nodeEntry.getValue());
}
@ -207,7 +209,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @return 节点若key已有对应节点则返回具有旧值的节点否则返回null
*/
@Override
public TreeEntryNode<K, V> putNode(K key, V value) {
public TreeEntryNode<K, V> putNode(final K key, final V value) {
TreeEntryNode<K, V> target = nodes.get(key);
if (ObjUtil.isNotNull(target)) {
final V oldVal = target.getValue();
@ -238,7 +240,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @param childValue 子节点的值
*/
@Override
public void putLinkedNodes(K parentKey, V parentValue, K childKey, V childValue) {
public void putLinkedNodes(final K parentKey, final V parentValue, final K childKey, final V childValue) {
linkNodes(parentKey, childKey, (parent, child) -> {
parent.setValue(parentValue);
child.setValue(childValue);
@ -257,7 +259,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @param childValue 子节点的值
*/
@Override
public void putLinkedNodes(K parentKey, K childKey, V childValue) {
public void putLinkedNodes(final K parentKey, final K childKey, final V childValue) {
linkNodes(parentKey, childKey, (parent, child) -> child.setValue(childValue));
}
@ -269,7 +271,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @param consumer 对父节点和子节点的操作允许为null
*/
@Override
public void linkNodes(K parentKey, K childKey, BiConsumer<TreeEntry<K, V>, TreeEntry<K, V>> consumer) {
public void linkNodes(final K parentKey, final K childKey, BiConsumer<TreeEntry<K, V>, TreeEntry<K, V>> consumer) {
consumer = ObjUtil.defaultIfNull(consumer, (parent, child) -> {
});
final TreeEntryNode<K, V> parentNode = nodes.computeIfAbsent(parentKey, t -> new TreeEntryNode<>(null, t));
@ -315,7 +317,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @param childKey 子节点
*/
@Override
public void unlinkNode(K parentKey, K childKey) {
public void unlinkNode(final K parentKey, final K childKey) {
final TreeEntryNode<K, V> childNode = nodes.get(childKey);
if (ObjUtil.isNull(childNode)) {
return;
@ -369,7 +371,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @param parent 节点的父节点
* @param key 节点的key
*/
public TreeEntryNode(TreeEntryNode<K, V> parent, K key) {
public TreeEntryNode(final TreeEntryNode<K, V> parent, final K key) {
this(parent, key, null);
}
@ -380,7 +382,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @param key 节点的key
* @param value 节点的value
*/
public TreeEntryNode(TreeEntryNode<K, V> parent, K key, V value) {
public TreeEntryNode(final TreeEntryNode<K, V> parent, final K key, final V value) {
this.parent = parent;
this.key = key;
this.value = value;
@ -432,7 +434,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @return 节点的旧value
*/
@Override
public V setValue(V value) {
public V setValue(final V value) {
final V oldVal = getValue();
this.value = value;
return oldVal;
@ -449,7 +451,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @return 遍历到的最后一个节点
*/
TreeEntryNode<K, V> traverseParentNodes(
boolean includeCurrent, Consumer<TreeEntryNode<K, V>> consumer, Predicate<TreeEntryNode<K, V>> breakTraverse) {
final boolean includeCurrent, final Consumer<TreeEntryNode<K, V>> consumer, Predicate<TreeEntryNode<K, V>> breakTraverse) {
breakTraverse = ObjUtil.defaultIfNull(breakTraverse, n -> false);
TreeEntryNode<K, V> curr = includeCurrent ? this : this.parent;
while (ObjUtil.isNotNull(curr)) {
@ -504,7 +506,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @return 指定父节点当不存在时返回null
*/
@Override
public TreeEntryNode<K, V> getParent(K key) {
public TreeEntryNode<K, V> getParent(final K key) {
return traverseParentNodes(false, p -> {
}, p -> p.equalsKey(key));
}
@ -516,7 +518,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @param nodeConsumer 对节点的处理
*/
@Override
public void forEachChild(boolean includeSelf, Consumer<TreeEntry<K, V>> nodeConsumer) {
public void forEachChild(final boolean includeSelf, final Consumer<TreeEntry<K, V>> nodeConsumer) {
traverseChildNodes(includeSelf, (index, child) -> nodeConsumer.accept(child), null);
}
@ -526,7 +528,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @param key 要比较的key
* @return 是否key一致
*/
public boolean equalsKey(K key) {
public boolean equalsKey(final K key) {
return ObjUtil.equals(getKey(), key);
}
@ -541,7 +543,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @return 遍历到的最后一个节点
*/
TreeEntryNode<K, V> traverseChildNodes(
boolean includeCurrent, BiConsumer<Integer, TreeEntryNode<K, V>> consumer, BiPredicate<Integer, TreeEntryNode<K, V>> breakTraverse) {
final boolean includeCurrent, final BiConsumer<Integer, TreeEntryNode<K, V>> consumer, BiPredicate<Integer, TreeEntryNode<K, V>> breakTraverse) {
breakTraverse = ObjUtil.defaultIfNull(breakTraverse, (i, n) -> false);
final Deque<List<TreeEntryNode<K, V>>> keyNodeDeque = ListUtil.ofLinked(ListUtil.of(this));
boolean needProcess = includeCurrent;
@ -577,7 +579,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @param child 子节点
* @throws IllegalArgumentException 当要添加的子节点已经是其自身父节点时抛出
*/
void addChild(TreeEntryNode<K, V> child) {
void addChild(final TreeEntryNode<K, V> child) {
if (containsChild(child.key)) {
return;
}
@ -605,7 +607,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
*
* @param key 子节点
*/
void removeDeclaredChild(K key) {
void removeDeclaredChild(final K key) {
final TreeEntryNode<K, V> child = children.get(key);
if (ObjUtil.isNull(child)) {
return;
@ -629,7 +631,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @return 节点
*/
@Override
public TreeEntryNode<K, V> getChild(K key) {
public TreeEntryNode<K, V> getChild(final K key) {
return traverseChildNodes(false, (i, c) -> {
}, (i, c) -> c.equalsKey(key));
}
@ -673,7 +675,7 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @return 是否
*/
@Override
public boolean equals(Object o) {
public boolean equals(final Object o) {
if (this == o) {
return true;
}
@ -701,8 +703,8 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @param value 复制的节点的值
* @return 节点
*/
TreeEntryNode<K, V> copy(V value) {
TreeEntryNode<K, V> copiedNode = new TreeEntryNode<>(this.parent, this.key, ObjUtil.defaultIfNull(value, this.value));
TreeEntryNode<K, V> copy(final V value) {
final TreeEntryNode<K, V> copiedNode = new TreeEntryNode<>(this.parent, this.key, ObjUtil.defaultIfNull(value, this.value));
copiedNode.children.putAll(children);
return copiedNode;
}
@ -718,10 +720,10 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
* @see #entrySet()
* @see #values()
*/
public static class EntryNodeWrapper<K, V, N extends TreeEntry<K, V>> implements Map.Entry<K, TreeEntry<K, V>> {
public static class EntryNodeWrapper<K, V, N extends TreeEntry<K, V>> implements Map.Entry<K, TreeEntry<K, V>>, Wrapper<N> {
private final N entryNode;
EntryNodeWrapper(N entryNode) {
EntryNodeWrapper(final N entryNode) {
this.entryNode = entryNode;
}
@ -736,9 +738,14 @@ public class LinkedForestMap<K, V> implements ForestMap<K, V> {
}
@Override
public TreeEntry<K, V> setValue(TreeEntry<K, V> value) {
public TreeEntry<K, V> setValue(final TreeEntry<K, V> value) {
throw new UnsupportedOperationException();
}
@Override
public N getRaw() {
return entryNode;
}
}
}

View File

@ -1,7 +1,7 @@
package cn.hutool.core.map;
import cn.hutool.core.builder.Builder;
import cn.hutool.core.lang.builder.Builder;
import java.util.Map;
import java.util.function.Supplier;

View File

@ -1,11 +1,11 @@
package cn.hutool.core.map;
import cn.hutool.core.classloader.ClassLoaderUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.getter.OptNullBasicTypeFromObjectGetter;
import cn.hutool.core.lang.getter.TypeGetter;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.classloader.ClassLoaderUtil;
import cn.hutool.core.text.StrUtil;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
@ -21,7 +21,7 @@ import java.util.Set;
* @author looly
* @since 3.2.0
*/
public class MapProxy implements Map<Object, Object>, OptNullBasicTypeFromObjectGetter<Object>, InvocationHandler, Serializable {
public class MapProxy implements Map<Object, Object>, TypeGetter<Object>, InvocationHandler, Serializable {
private static final long serialVersionUID = 1L;
@SuppressWarnings("rawtypes")
@ -32,7 +32,7 @@ public class MapProxy implements Map<Object, Object>, OptNullBasicTypeFromObject
* 此类对Map做一次包装提供各种getXXX方法
*
* @param map 被代理的Map
* @return {@link MapProxy}
* @return {@code MapProxy}
*/
public static MapProxy of(final Map<?, ?> map) {
return (map instanceof MapProxy) ? (MapProxy) map : new MapProxy(map);
@ -47,10 +47,10 @@ public class MapProxy implements Map<Object, Object>, OptNullBasicTypeFromObject
this.map = map;
}
@SuppressWarnings("unchecked")
@Override
public Object getObj(final Object key, final Object defaultValue) {
final Object value = map.get(key);
return null != value ? value : defaultValue;
return map.getOrDefault(key, defaultValue);
}
@Override

View File

@ -1,6 +1,7 @@
package cn.hutool.core.map;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.func.Wrapper;
import cn.hutool.core.util.ObjUtil;
import java.io.IOException;
@ -21,7 +22,7 @@ import java.util.function.Supplier;
* @author looly
* @since 4.3.3
*/
public class MapWrapper<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, Serializable, Cloneable {
public class MapWrapper<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, Wrapper<Map<K, V>>, Serializable, Cloneable {
private static final long serialVersionUID = -7524578042008586382L;
/**
@ -64,6 +65,7 @@ public class MapWrapper<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, S
*
* @return Map
*/
@Override
public Map<K, V> getRaw() {
return this.raw;
}

View File

@ -0,0 +1,141 @@
package cn.hutool.core.map.multi;
import cn.hutool.core.collection.CollUtil;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
/**
* 支持处理无向图结构的{@link Map}本质上是基于{@link SetValueMap}实现的邻接表
*
* @param <T> 节点类型
* @author huangchengxing
* @since 6.0.0
*/
public class Graph<T> extends SetValueMap<T, T> {
/**
* 添加边
*
* @param target1 节点
* @param target2 节点
*/
public void putEdge(final T target1, final T target2) {
this.putValue(target1, target2);
this.putValue(target2, target1);
}
/**
* 是否存在边
*
* @param target1 节点
* @param target2 节点
* @return 是否
*/
public boolean containsEdge(final T target1, final T target2) {
return this.getValues(target1).contains(target2)
&& this.getValues(target2).contains(target1);
}
/**
* 移除边
*
* @param target1 节点
* @param target2 节点
*/
public void removeEdge(final T target1, final T target2) {
this.removeValue(target1, target2);
this.removeValue(target2, target1);
}
/**
* 移除节点并删除该节点与其他节点之间连成的边
*
* @param target 目标对象
*/
public void removePoint(final T target) {
final Collection<T> associatedPoints = this.remove(target);
if (CollUtil.isNotEmpty(associatedPoints)) {
associatedPoints.forEach(p -> this.removeValue(p, target));
}
}
/**
* 两节点是否存在直接或间接的关联
*
* @param target1 节点
* @param target2 节点
* @return 两节点是否存在关联
*/
public boolean containsAssociation(final T target1, final T target2) {
if (!this.containsKey(target1) || !this.containsKey(target2)) {
return false;
}
final AtomicBoolean flag = new AtomicBoolean(false);
visitAssociatedPoints(target1, t -> {
if (Objects.equals(t, target2)) {
flag.set(true);
return true;
}
return false;
});
return flag.get();
}
/**
* 按广度优先获得节点的所有直接或间接关联的节点节点默认按添加顺序排序
*
* @param target 节点
* @param includeTarget 是否包含查询节点
* @return 节点的所有关联节点
*/
public Collection<T> getAssociatedPoints(final T target, final boolean includeTarget) {
final Set<T> points = visitAssociatedPoints(target, t -> false);
if (!includeTarget) {
points.remove(target);
}
return points;
}
/**
* 获取节点的邻接节点
*
* @param target 节点
* @return 邻接节点
*/
public Collection<T> getAdjacentPoints(final T target) {
return this.getValues(target);
}
/**
* 按广度优先访问节点的所有关联节点
*/
private Set<T> visitAssociatedPoints(final T key, final Predicate<T> breaker) {
if (!this.containsKey(key)) {
return Collections.emptySet();
}
final Set<T> accessed = new HashSet<>();
final Deque<T> deque = new LinkedList<>();
deque.add(key);
while (!deque.isEmpty()) {
// 访问节点
final T t = deque.removeFirst();
if (accessed.contains(t)) {
continue;
}
accessed.add(t);
// 若符合条件则中断循环
if (breaker.test(t)) {
break;
}
// 获取邻接节点
final Collection<T> neighbours = this.getValues(t);
if (!neighbours.isEmpty()) {
deque.addAll(neighbours);
}
}
return accessed;
}
}

View File

@ -22,7 +22,7 @@ public class ListValueMap<K, V> extends AbsCollValueMap<K, V> {
*
* @param mapFactory 创建集合的工厂反方
*/
public ListValueMap(Supplier<Map<K, Collection<V>>> mapFactory) {
public ListValueMap(final Supplier<Map<K, Collection<V>>> mapFactory) {
super(mapFactory);
}
@ -31,7 +31,7 @@ public class ListValueMap<K, V> extends AbsCollValueMap<K, V> {
*
* @param map 提供数据的原始集合
*/
public ListValueMap(Map<K, Collection<V>> map) {
public ListValueMap(final Map<K, Collection<V>> map) {
super(map);
}

View File

@ -1,6 +1,6 @@
package cn.hutool.core.map.multi;
import cn.hutool.core.builder.Builder;
import cn.hutool.core.lang.builder.Builder;
import cn.hutool.core.collection.iter.ComputeIter;
import cn.hutool.core.collection.iter.IterUtil;
import cn.hutool.core.collection.iter.TransIter;

View File

@ -22,6 +22,9 @@ import java.util.regex.Matcher;
*/
public class Ipv4Util {
/**
* 本地IP127.0.0.1
*/
public static final String LOCAL_IP = "127.0.0.1";
/**
@ -162,9 +165,6 @@ public class Ipv4Util {
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!");
}

View File

@ -12,7 +12,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* @since 4.0.3
*
*/
public class LocalPortGenerater implements Serializable{
public class LocalPortGenerator implements Serializable{
private static final long serialVersionUID = 1L;
/** 备选的本地端口 */
@ -23,7 +23,7 @@ public class LocalPortGenerater implements Serializable{
*
* @param beginPort 起始端口号
*/
public LocalPortGenerater(final int beginPort) {
public LocalPortGenerator(final int beginPort) {
alternativePort = new AtomicInteger(beginPort);
}

View File

@ -48,9 +48,12 @@ import java.util.function.Predicate;
*/
public class NetUtil {
/**
* 本地IP
*/
public final static String LOCAL_IP = Ipv4Util.LOCAL_IP;
public static String localhostName;
private static String localhostName;
/**
* 默认最小端口1024
@ -841,8 +844,7 @@ public class NetUtil {
}
/**
* 获取DNS信息如TXT信息
*
* 获取DNS信息如TXT信息<br>
* <pre class="code">
* NetUtil.attrNames("hutool.cn", "TXT")
* </pre>

View File

@ -1,4 +1,4 @@
package cn.hutool.core.net;
package cn.hutool.core.net.ssl;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedTrustManager;

View File

@ -1,6 +1,6 @@
package cn.hutool.core.net;
package cn.hutool.core.net.ssl;
import cn.hutool.core.builder.Builder;
import cn.hutool.core.lang.builder.Builder;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.text.StrUtil;

View File

@ -1,4 +1,4 @@
package cn.hutool.core.net;
package cn.hutool.core.net.ssl;
/**
* SSL或TLS协议

View File

@ -1,4 +1,4 @@
package cn.hutool.core.net;
package cn.hutool.core.net.ssl;
import cn.hutool.core.io.IORuntimeException;

View File

@ -0,0 +1,6 @@
/**
* SSL相关封装
*
* @author looly
*/
package cn.hutool.core.net.ssl;

View File

@ -1,4 +1,4 @@
package cn.hutool.core.net;
package cn.hutool.core.net.url;
import cn.hutool.core.codec.PercentCodec;

View File

@ -1,4 +1,4 @@
package cn.hutool.core.net;
package cn.hutool.core.net.url;
import cn.hutool.core.codec.PercentCodec;

Some files were not shown because too many files have changed in this diff Show More