mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
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:
commit
2c2ad12972
@ -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`,并确认通过
|
||||
|
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -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`,并确认通过
|
@ -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}
|
||||
*/
|
||||
|
@ -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},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
/**
|
||||
* 内部类分界符: '$'
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package cn.hutool.core.lang.hash;
|
||||
package cn.hutool.core.codec;
|
||||
|
||||
/**
|
||||
* 128位数字表示,分高位和低位
|
@ -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的安全字符
|
||||
*
|
||||
|
@ -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();
|
||||
|
@ -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();
|
@ -1,4 +1,4 @@
|
||||
package cn.hutool.core.lang.hash;
|
||||
package cn.hutool.core.codec.hash;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
@ -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);
|
||||
}
|
||||
}
|
8
hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash32.java → hutool-core/src/main/java/cn/hutool/core/codec/hash/Hash32.java
Normal file → Executable file
8
hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash32.java → hutool-core/src/main/java/cn/hutool/core/codec/hash/Hash32.java
Normal file → Executable 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);
|
||||
}
|
||||
}
|
8
hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash64.java → hutool-core/src/main/java/cn/hutool/core/codec/hash/Hash64.java
Normal file → Executable file
8
hutool-core/src/main/java/cn/hutool/core/lang/hash/Hash64.java → hutool-core/src/main/java/cn/hutool/core/codec/hash/Hash64.java
Normal file → Executable 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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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 32bit、64bit、128bit 哈希算法实现<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) {
|
@ -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
|
||||
|
||||
/**
|
||||
* 计算汉明距离
|
||||
*
|
@ -4,4 +4,4 @@
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.core.lang.hash;
|
||||
package cn.hutool.core.codec.hash;
|
@ -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>
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 索引比较器
|
||||
|
25
hutool-core/src/main/java/cn/hutool/core/comparator/StrLengthComparator.java
Executable file
25
hutool-core/src/main/java/cn/hutool/core/comparator/StrLengthComparator.java
Executable 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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”
|
||||
|
@ -18,7 +18,7 @@ public interface Converter {
|
||||
* 如果类型无法确定,将读取默认值的类型做为目标类型
|
||||
*
|
||||
* @param targetType 目标Type,非泛型类使用
|
||||
* @param value 原始值
|
||||
* @param value 原始值,如果对象实现了此接口,则value为this
|
||||
* @return 转换后的值
|
||||
* @throws ConvertException 转换无法正常完成或转换异常时抛出此异常
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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无法确认换行
|
||||
|
@ -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");
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
package cn.hutool.core.io;
|
||||
|
||||
/**
|
||||
* 行处理器
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface LineHandler {
|
||||
/**
|
||||
* 处理一行数据,可以编辑后存入指定地方
|
||||
* @param line 行
|
||||
*/
|
||||
void handle(String line);
|
||||
}
|
@ -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 不抛出
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
@ -5,4 +5,4 @@
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.core.builder;
|
||||
package cn.hutool.core.lang.builder;
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段名称,主要用于方法名称截取,方法名称必须为getXXX、isXXX、setXXX
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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) -> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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) {
|
||||
|
17
hutool-core/src/main/java/cn/hutool/core/lang/func/Wrapper.java
Executable file
17
hutool-core/src/main/java/cn/hutool/core/lang/func/Wrapper.java
Executable 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();
|
||||
}
|
@ -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 -------------------------------*/
|
||||
}
|
@ -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 -------------------------------*/
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 -------------------------------*/
|
||||
}
|
@ -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 -------------------------------*/
|
||||
}
|
@ -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 -------------------------------*/
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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 -------------------------------*/
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
141
hutool-core/src/main/java/cn/hutool/core/map/multi/Graph.java
Normal file
141
hutool-core/src/main/java/cn/hutool/core/map/multi/Graph.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -22,6 +22,9 @@ import java.util.regex.Matcher;
|
||||
*/
|
||||
public class Ipv4Util {
|
||||
|
||||
/**
|
||||
* 本地IP:127.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!");
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
@ -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;
|
@ -1,4 +1,4 @@
|
||||
package cn.hutool.core.net;
|
||||
package cn.hutool.core.net.ssl;
|
||||
|
||||
/**
|
||||
* SSL或TLS协议
|
@ -1,4 +1,4 @@
|
||||
package cn.hutool.core.net;
|
||||
package cn.hutool.core.net.ssl;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
|
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* SSL相关封装
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
package cn.hutool.core.net.ssl;
|
@ -1,4 +1,4 @@
|
||||
package cn.hutool.core.net;
|
||||
package cn.hutool.core.net.url;
|
||||
|
||||
import cn.hutool.core.codec.PercentCodec;
|
||||
|
2
hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java → hutool-core/src/main/java/cn/hutool/core/net/url/RFC3986.java
Executable file → Normal file
2
hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java → hutool-core/src/main/java/cn/hutool/core/net/url/RFC3986.java
Executable file → Normal 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
Loading…
x
Reference in New Issue
Block a user