addd Tree

This commit is contained in:
Looly 2020-03-09 18:27:25 +08:00
parent e2af8b96a5
commit dbcca73dc2
17 changed files with 695 additions and 512 deletions

View File

@ -7,6 +7,7 @@
### 新特性
* 【core 】 修改FastDateParser策略与JDK保持一致issue#I1AXIN@Gitee
* 【core 】 增加tree树状结构pr#100@Gitee
### Bug修复
* 【setting】 修复Props.toBean方法null的问题
* 【core 】 修复DataUtil.parseLocalDateTime无时间部分报错问题issue#I1B18H@Gitee

View File

@ -23,7 +23,7 @@ public class ConsistentHash<T> implements Serializable{
/** 复制的节点个数 */
private final int numberOfReplicas;
/** 一致性Hash环 */
private final SortedMap<Integer, T> circle = new TreeMap<Integer, T>();
private final SortedMap<Integer, T> circle = new TreeMap<>();
/**
* 构造使用Java默认的Hash算法

View File

@ -959,7 +959,18 @@ public class Validator {
* @return 是否为汉字
*/
public static boolean isChinese(CharSequence value) {
return isMactchRegex("^" + ReUtil.RE_CHINESE + "+$", value);
return isMactchRegex("^" + ReUtil.RE_CHINESES + "$", value);
}
/**
* 验证是否包含汉字
*
* @param value
* @return 是否包含汉字
* @since 5.2.1
*/
public static boolean hasChinese(CharSequence value) {
return ReUtil.contains(ReUtil.RE_CHINESES, value);
}
/**

View File

@ -1,15 +0,0 @@
package cn.hutool.core.lang.tree;
/**
* 树节点转换器 可以参考{{@link DefaultConverter}}
*
* @author liangbaikai
*/
public interface Convert<T, TreeNodeMap> {
/**
* @param object 源数据实体
* @param treeNode 树节点实体
*/
void convert(T object, TreeNodeMap treeNode);
}

View File

@ -1,20 +0,0 @@
package cn.hutool.core.lang.tree;
/**
* 默认的简单转换器
*
* @author liangbaikai
*/
public class DefaultConverter implements Convert<Node, TreeNodeMap> {
@Override
public void convert(Node object, TreeNodeMap treeNode) {
treeNode.setId(object.getId());
treeNode.setParentId(object.getPid());
treeNode.setWeight(object.getWeight());
treeNode.setName(object.getName());
//扩展字段
// treeNode.extra("other",11);
// treeNode.extra("other2",object.getXXX);
}
}

View File

@ -1,71 +0,0 @@
package cn.hutool.core.lang.tree;
/**
* 树节点 每个属性都可以在{@link TreeNodeConfig}中被重命名,在你的项目里它可以是部门实体 地区实体等任意类树节点实体
* 类树节点实体: 包含key父Key.不限于这些属性的可以构造成一颗树的实体对象
*
* @author liangbaikai
*/
public class Node {
// ID
private String id;
// 上级ID
private String pid;
// 名称
private String name;
// 顺序 越小优先级越高 默认0
private Comparable weight = 0;
public Node() {
}
public Node(String id, String pid, String name, Comparable weight) {
this.id = id;
this.pid = pid;
this.name = name;
if (weight != null) {
this.weight = weight;
}
}
public Comparable getWeight() {
return weight;
}
public void setWeight(Comparable weight) {
this.weight = weight;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,121 @@
package cn.hutool.core.lang.tree;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import java.util.LinkedHashMap;
import java.util.List;
/**
* 通过转换器将你的实体转化为TreeNodeMap节点实体 属性都存在此处,属性有序可支持排序
*
* @param <T> ID类型
* @author liangbaikai
* @since 5.2.1
*/
public class Tree<T> extends LinkedHashMap<String, Object> implements Comparable<Tree<T>> {
private static final long serialVersionUID = 1L;
private TreeNodeConfig treeNodeConfig;
public Tree() {
this(null);
}
/**
* 构造
*
* @param treeNodeConfig TreeNode配置
*/
public Tree(TreeNodeConfig treeNodeConfig) {
super();
this.treeNodeConfig = ObjectUtil.defaultIfNull(
treeNodeConfig, TreeNodeConfig.DEFAULT_CONFIG);
}
/**
* 获取节点ID
*
* @return 节点ID
*/
@SuppressWarnings("unchecked")
public T getId() {
return (T) this.get(treeNodeConfig.getIdKey());
}
/**
* 设置节点ID
*
* @param id 节点ID
* @return this
*/
public Tree<T> setId(T id) {
this.put(treeNodeConfig.getIdKey(), id);
return this;
}
/**
* 获取父节点ID
*
* @return 父节点ID
*/
@SuppressWarnings("unchecked")
public T getParentId() {
return (T) this.get(treeNodeConfig.getParentIdKey());
}
public Tree<T> setParentId(T parentId) {
this.put(treeNodeConfig.getParentIdKey(), parentId);
return this;
}
@SuppressWarnings("unchecked")
public T getName() {
return (T) this.get(treeNodeConfig.getNameKey());
}
public Tree<T> setName(Object name) {
this.put(treeNodeConfig.getNameKey(), name);
return this;
}
public Comparable<?> getWeight() {
return (Comparable<?>) this.get(treeNodeConfig.getWeightKey());
}
public Tree<T> setWeight(Comparable<?> weight) {
this.put(treeNodeConfig.getWeightKey(), weight);
return this;
}
@SuppressWarnings("unchecked")
public List<Tree<T>> getChildren() {
return (List<Tree<T>>) this.get(treeNodeConfig.getChildrenKey());
}
public void setChildren(List<Tree<T>> children) {
this.put(treeNodeConfig.getChildrenKey(), children);
}
/**
* 扩展属性
*
* @param key
* @param value 扩展值
*/
public void putExtra(String key, Object value) {
Assert.notEmpty(key, "Key must be not empty !");
this.put(key, value);
}
@SuppressWarnings({"rawtypes", "unchecked", "NullableProblems"})
@Override
public int compareTo(Tree<T> tree) {
final Comparable weight = this.getWeight();
if (null != weight) {
final Comparable weightOther = tree.getWeight();
return weight.compareTo(weightOther);
}
return 0;
}
}

View File

@ -0,0 +1,146 @@
package cn.hutool.core.lang.tree;
/**
* 树节点 每个属性都可以在{@link TreeNodeConfig}中被重命名<br>
* 在你的项目里它可以是部门实体地区实体等任意类树节点实体
* 类树节点实体: 包含key父Key.不限于这些属性的可以构造成一颗树的实体对象
*
* @author liangbaikai
*/
public class TreeNode<T> implements Comparable<Tree<T>> {
/**
* ID
*/
private T id;
/**
* 父节点ID
*/
private T parentId;
/**
* 名称
*/
private CharSequence name;
/**
* 顺序 越小优先级越高 默认0
*/
private Comparable<?> weight = 0;
/**
* 空构造
*/
public TreeNode() {
}
/**
* 构造
*
* @param id ID
* @param parentId 父节点ID
* @param name 名称
* @param weight 权重
*/
public TreeNode(T id, T parentId, String name, Comparable<?> weight) {
this.id = id;
this.parentId = parentId;
this.name = name;
if (weight != null) {
this.weight = weight;
}
}
/**
* 获取ID
*
* @return ID
*/
public T getId() {
return id;
}
/**
* 设置ID
*
* @param id ID
*/
public void setId(T id) {
this.id = id;
}
/**
* 获取父节点ID
*
* @return 父节点ID
*/
public T getParentId() {
return this.parentId;
}
/**
* 设置父节点ID
*
* @param parentId 父节点ID
* @return 父节点ID
*/
public TreeNode<T> setParentId(T parentId) {
this.parentId = parentId;
return this;
}
/**
* 获取节点标签名称
*
* @return 节点标签名称
*/
public CharSequence getName() {
return name;
}
/**
* 设置节点标签名称
*
* @param name 节点标签名称
* @return this
*/
public TreeNode<T> setName(CharSequence name) {
this.name = name;
return this;
}
/**
* 获取权重
*
* @return 权重
*/
public Comparable<?> getWeight() {
return weight;
}
/**
* 设置权重
*
* @param weight 权重
* @return this
*/
public TreeNode<T> setWeight(Comparable<?> weight) {
this.weight = weight;
return this;
}
@SuppressWarnings({"unchecked", "rawtypes", "NullableProblems"})
@Override
public int compareTo(Tree tree) {
final Comparable weight = this.getWeight();
if (null != weight) {
final Comparable weightOther = tree.getWeight();
return weight.compareTo(weightOther);
}
return 0;
}
}

View File

@ -7,111 +7,139 @@ package cn.hutool.core.lang.tree;
*/
public class TreeNodeConfig {
/**
* 默认属性配置对象
*/
private static TreeNodeConfig defaultConfig = new TreeNodeConfig();
/**
* 默认属性配置对象
*/
public static TreeNodeConfig DEFAULT_CONFIG = new TreeNodeConfig();
// 树节点默认属性常量 当然你可以重新设置
/**
* 节点 id 名称
*/
static final String TREE_ID = "id";
/**
* 节点 parentId 父id名称
*/
static final String TREE_PARENT_ID = "parentId";
/**
* 节点 name 显示名称
*/
static final String TREE_NAME = "name";
/**
* 节点 weight 顺序名称
*/
static final String TREE_WEIGHT = "weight";
/**
* 节点 name 子节点名称
*/
static final String TREE_CHILDREN = "children";
// 属性名配置字段
private String idKey = "id";
private String parentIdKey = "parentId";
private String weightKey = "weight";
private String nameKey = "name";
private String childrenKey = "children";
// 可以配置递归深度 从0开始计算 默认此配置为空,即不限制
private Integer deep;
static {
//init
defaultConfig.setIdKey(TREE_ID);
defaultConfig.setWeightKey(TREE_WEIGHT);
defaultConfig.setNameKey(TREE_NAME);
defaultConfig.setChildrenKey(TREE_CHILDREN);
defaultConfig.setParentIdKey(TREE_PARENT_ID);
}
/**
* 获取ID对应的名称
*
* @return ID对应的名称
*/
public String getIdKey() {
return this.idKey;
}
// 属性名配置字段
private String idKey;
private String parentIdKey;
private String weightKey;
private String nameKey;
private String childrenKey;
/**
* 设置ID对应的名称
*
* @param idKey ID对应的名称
* @return this
*/
public TreeNodeConfig setIdKey(String idKey) {
this.idKey = idKey;
return this;
}
// 可以配置递归深度 从0开始计算 默认此配置为空,即不限制
private Integer deep;
/**
* 获取权重对应的名称
*
* @return 权重对应的名称
*/
public String getWeightKey() {
return this.weightKey;
}
/**
* 设置权重对应的名称
*
* @param weightKey 权重对应的名称
* @return this
*/
public TreeNodeConfig setWeightKey(String weightKey) {
this.weightKey = weightKey;
return this;
}
/**
* 获取节点名对应的名称
*
* @return 节点名对应的名称
*/
public String getNameKey() {
return this.nameKey;
}
/**
* 设置节点名对应的名称
*
* @param nameKey 节点名对应的名称
* @return this
*/
public TreeNodeConfig setNameKey(String nameKey) {
this.nameKey = nameKey;
return this;
}
/**
* 获取子点对应的名称
*
* @return 子点对应的名称
*/
public String getChildrenKey() {
return this.childrenKey;
}
/**
* 设置子点对应的名称
*
* @param childrenKey 子点对应的名称
* @return this
*/
public TreeNodeConfig setChildrenKey(String childrenKey) {
this.childrenKey = childrenKey;
return this;
}
/**
* 获取父节点ID对应的名称
*
* @return 父点对应的名称
*/
public String getParentIdKey() {
return this.parentIdKey;
}
public String getIdKey() {
return getOrDefault(idKey, TREE_ID);
}
/**
* 设置父点对应的名称
*
* @param parentIdKey 父点对应的名称
* @return this
*/
public TreeNodeConfig setParentIdKey(String parentIdKey) {
this.parentIdKey = parentIdKey;
return this;
}
public void setIdKey(String idKey) {
this.idKey = idKey;
}
public String getWeightKey() {
return getOrDefault(weightKey, TREE_WEIGHT);
}
public void setWeightKey(String weightKey) {
this.weightKey = weightKey;
}
public String getNameKey() {
return getOrDefault(nameKey, TREE_NAME);
}
public void setNameKey(String nameKey) {
this.nameKey = nameKey;
}
public String getChildrenKey() {
return getOrDefault(childrenKey, TREE_CHILDREN);
}
public void setChildrenKey(String childrenKey) {
this.childrenKey = childrenKey;
}
public String getParentIdKey() {
return getOrDefault(parentIdKey, TREE_PARENT_ID);
}
public void setParentIdKey(String parentIdKey) {
this.parentIdKey = parentIdKey;
}
public String getOrDefault(String key, String defaultKey) {
if (key == null) {
return defaultKey;
}
return key;
}
public Integer getDeep() {
return deep;
}
public void setDeep(Integer deep) {
this.deep = deep;
}
public static TreeNodeConfig getDefaultConfig() {
return defaultConfig;
}
/**
* 获取递归深度
*
* @return 递归深度
*/
public Integer getDeep() {
return this.deep;
}
/**
* 设置递归深度
*
* @param deep 递归深度
* @return this
*/
public TreeNodeConfig setDeep(Integer deep) {
this.deep = deep;
return this;
}
}

View File

@ -1,94 +0,0 @@
package cn.hutool.core.lang.tree;
import java.util.LinkedHashMap;
import java.util.List;
/**
* 通过转换器将你的实体转化为TreeNodeMap节点实体 属性都存在此处,属性有序可支持排序
*
* @author liangbaikai
*/
public class TreeNodeMap extends LinkedHashMap implements Comparable<TreeNodeMap> {
private static final long serialVersionUID = 8376668307601977428L;
private TreeNodeConfig treeNodeConfig;
public TreeNodeMap() {
this.treeNodeConfig = TreeNodeConfig.getDefaultConfig();
}
public TreeNodeMap(TreeNodeConfig treeNodeConfig) {
this.treeNodeConfig = treeNodeConfig;
}
public <T> T getId() {
return (T) super.get(treeNodeConfig.getIdKey());
}
public void setId(String id) {
super.put(treeNodeConfig.getIdKey(), id);
}
public <T> T getParentId() {
return (T) super.get(treeNodeConfig.getParentIdKey());
}
public void setParentId(String parentId) {
super.put(treeNodeConfig.getParentIdKey(), parentId);
}
public <T> T getName() {
return (T) super.get(treeNodeConfig.getNameKey());
}
public void setName(String name) {
super.put(treeNodeConfig.getNameKey(), name);
}
public Comparable getWeight() {
return (Comparable) super.get(treeNodeConfig.getWeightKey());
}
public TreeNodeMap setWeight(Comparable weight) {
super.put(treeNodeConfig.getWeightKey(), weight);
return this;
}
public List<TreeNodeMap> getChildren() {
return (List<TreeNodeMap>) super.get(treeNodeConfig.getChildrenKey());
}
public void setChildren(List<TreeNodeMap> children) {
super.put(treeNodeConfig.getChildrenKey(), children);
}
/**
* 扩展属性
*
* @param key
* @param value
*/
public void extra(String key, Object value) {
if (key != null && key != "") {
super.put(key, value);
}
}
/**
* 可以支持排序
*
* @param treeNodeMap weight值越小 优先级越高 默认为0
* @return
*/
@Override
public int compareTo(TreeNodeMap treeNodeMap) {
try {
//可能为空
int res = this.getWeight().compareTo(treeNodeMap.getWeight());
return res;
} catch (NullPointerException e) {
return 0;
}
}
}

View File

@ -1,6 +1,8 @@
package cn.hutool.core.lang.tree;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.tree.parser.DefaultNodeParser;
import cn.hutool.core.lang.tree.parser.NodeParser;
import java.util.List;
import java.util.stream.Collectors;
@ -12,82 +14,89 @@ import java.util.stream.Collectors;
*/
public class TreeUtil {
/**
* 树构建
*/
public static List<TreeNodeMap> build(List<Node> list, Object parentId) {
return build(list, parentId, TreeNodeConfig.getDefaultConfig(), new DefaultConverter());
}
/**
* 树构建
*/
public static List<Tree<Integer>> build(List<TreeNode<Integer>> list) {
return build(list, 0);
}
/**
* 树构建
*/
public static <T> List<TreeNodeMap> build(List<T> list, Object parentId, Convert<T, TreeNodeMap> convert) {
return build(list, parentId, TreeNodeConfig.getDefaultConfig(), convert);
}
/**
* 树构建
*/
public static <T> List<Tree<T>> build(List<TreeNode<T>> list, T parentId) {
return build(list, parentId, TreeNodeConfig.DEFAULT_CONFIG, new DefaultNodeParser<>());
}
/**
* 树构建
*
* @param list 源数据集合
* @param parentId 最顶层父id值 一般为 0 之类
* @param treeNodeConfig 配置
* @param convert 转换器
* @param <T> 转换的实体 为数据源里的对象类型
* @return
*/
public static <T> List<TreeNodeMap> build(List<T> list, Object parentId, TreeNodeConfig treeNodeConfig, Convert<T, TreeNodeMap> convert) {
List<TreeNodeMap> treeNodes = CollectionUtil.newArrayList();
for (T obj : list) {
TreeNodeMap treeNode = new TreeNodeMap(treeNodeConfig);
convert.convert(obj, treeNode);
treeNodes.add(treeNode);
}
/**
* 树构建
*/
public static <T, E> List<Tree<E>> build(List<T> list, E parentId, NodeParser<T, E> nodeParser) {
return build(list, parentId, TreeNodeConfig.DEFAULT_CONFIG, nodeParser);
}
List<TreeNodeMap> finalTreeNodes = CollectionUtil.newArrayList();
for (TreeNodeMap treeNode : treeNodes) {
if (parentId.equals(treeNode.getParentId())) {
finalTreeNodes.add(treeNode);
innerBuild(treeNodes, treeNode, 0, treeNodeConfig.getDeep());
}
}
// 内存每层已经排过了 这是最外层排序
finalTreeNodes = finalTreeNodes.stream().sorted().collect(Collectors.toList());
return finalTreeNodes;
}
/**
* 树构建
*
* @param list 源数据集合
* @param parentId 最顶层父id值 一般为 0 之类
* @param treeNodeConfig 配置
* @param nodeParser 转换器
* @param <T> 转换的实体 为数据源里的对象类型
* @return List
*/
public static <T, E> List<Tree<E>> build(List<T> list, E parentId, TreeNodeConfig treeNodeConfig, NodeParser<T, E> nodeParser) {
List<Tree<E>> treeNodes = CollectionUtil.newArrayList();
for (T obj : list) {
Tree<E> treeNode = new Tree<>(treeNodeConfig);
nodeParser.parse(obj, treeNode);
treeNodes.add(treeNode);
}
/**
* 递归处理
*
* @param treeNodes 数据集合
* @param parentNode 当前节点
* @param deep 已递归深度
* @param maxDeep 最大递归深度 可能为null即不限制
*/
private static void innerBuild(List<TreeNodeMap> treeNodes, TreeNodeMap parentNode, int deep, Integer maxDeep) {
List<Tree<E>> finalTreeNodes = CollectionUtil.newArrayList();
for (Tree<E> treeNode : treeNodes) {
if (parentId.equals(treeNode.getParentId())) {
finalTreeNodes.add(treeNode);
innerBuild(treeNodes, treeNode, 0, treeNodeConfig.getDeep());
}
}
// 内存每层已经排过了 这是最外层排序
finalTreeNodes = finalTreeNodes.stream().sorted().collect(Collectors.toList());
return finalTreeNodes;
}
if (CollectionUtil.isEmpty(treeNodes)) {
return;
}
//maxDeep 可能为空
if (maxDeep != null && deep >= maxDeep) {
return;
}
/**
* 递归处理
*
* @param treeNodes 数据集合
* @param parentNode 当前节点
* @param deep 已递归深度
* @param maxDeep 最大递归深度 可能为null即不限制
*/
private static <T> void innerBuild(List<Tree<T>> treeNodes, Tree<T> parentNode, int deep, Integer maxDeep) {
// 每层排序 TreeNodeMap 实现了Comparable接口
treeNodes = treeNodes.stream().sorted().collect(Collectors.toList());
for (TreeNodeMap childNode : treeNodes) {
if (parentNode.getId().equals(childNode.getParentId())) {
List<TreeNodeMap> children = parentNode.getChildren();
if (children == null) {
children = CollectionUtil.newArrayList();
parentNode.setChildren(children);
}
children.add(childNode);
childNode.setParentId(parentNode.getId());
innerBuild(treeNodes, childNode, deep + 1, maxDeep);
}
}
}
if (CollectionUtil.isEmpty(treeNodes)) {
return;
}
//maxDeep 可能为空
if (maxDeep != null && deep >= maxDeep) {
return;
}
// 每层排序 TreeNodeMap 实现了Comparable接口
treeNodes = treeNodes.stream().sorted().collect(Collectors.toList());
for (Tree<T> childNode : treeNodes) {
if (parentNode.getId().equals(childNode.getParentId())) {
List<Tree<T>> children = parentNode.getChildren();
if (children == null) {
children = CollectionUtil.newArrayList();
parentNode.setChildren(children);
}
children.add(childNode);
childNode.setParentId(parentNode.getId());
innerBuild(treeNodes, childNode, deep + 1, maxDeep);
}
}
}
}

View File

@ -1 +1,15 @@
/**
* 提供通用树生成特点
* <pre>
* 1每个字段可自定义
* 2支持排序 树深度配置,自定义转换器等
* 3支持额外属性扩展
* 4贴心 许多属性,特性都有默认值处理
* 5使用简单 可一行代码生成树
* 6代码简洁轻量无额外依赖
* <pre/>
*
* @author liangbaikaihttps://gitee.com/liangbaikai00/
* @since 5.2.1
*/
package cn.hutool.core.lang.tree;

View File

@ -0,0 +1,25 @@
package cn.hutool.core.lang.tree.parser;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.Tree;
/**
* 默认的简单转换器
*
* @param <T> ID类型
* @author liangbaikai
*/
public class DefaultNodeParser<T> implements NodeParser<TreeNode<T>, T> {
@Override
public void parse(TreeNode<T> object, Tree<T> treeNode) {
treeNode.setId(object.getId());
treeNode.setParentId(object.getParentId());
treeNode.setWeight(object.getWeight());
treeNode.setName(object.getName());
//扩展字段
// treeNode.extra("other",11);
// treeNode.extra("other2",object.getXXX);
}
}

View File

@ -0,0 +1,18 @@
package cn.hutool.core.lang.tree.parser;
import cn.hutool.core.lang.tree.Tree;
/**
* 树节点解析器 可以参考{@link DefaultNodeParser}
*
* @param <T> 转换的实体 为数据源里的对象类型
* @author liangbaikai
*/
public interface NodeParser<T, E> {
/**
* @param object 源数据实体
* @param treeNode 树节点实体
*/
void parse(T object, Tree<E> treeNode);
}

View File

@ -1,6 +1,6 @@
package cn.hutool.core.util;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Holder;
@ -8,30 +8,41 @@ import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.lang.func.Func1;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 正则相关工具类<br>
* 常用正则请见 {@link Validator}
*
*
* @author xiaoleilu
*/
public class ReUtil {
/** 正则表达式匹配中文汉字 */
/**
* 正则表达式匹配中文汉字
*/
public final static String RE_CHINESE = "[\u4E00-\u9FFF]";
/** 正则表达式匹配中文字符串 */
/**
* 正则表达式匹配中文字符串
*/
public final static String RE_CHINESES = RE_CHINESE + "+";
/** 正则中需要被转义的关键字 */
public final static Set<Character> RE_KEYS = CollectionUtil.newHashSet(new Character[] { '$', '(', ')', '*', '+', '.', '[', ']', '?', '\\', '^', '{', '}', '|' });
/**
* 正则中需要被转义的关键字
*/
public final static Set<Character> RE_KEYS = CollUtil.newHashSet('$', '(', ')', '*', '+', '.', '[', ']', '?', '\\', '^', '{', '}', '|');
/**
* 获得匹配的字符串获得正则中分组0的内容
*
* @param regex 匹配的正则
*
* @param regex 匹配的正则
* @param content 被匹配的内容
* @return 匹配后得到的字符串未匹配返回null
* @since 3.1.2
@ -42,8 +53,8 @@ public class ReUtil {
/**
* 获得匹配的字符串获得正则中分组1的内容
*
* @param regex 匹配的正则
*
* @param regex 匹配的正则
* @param content 被匹配的内容
* @return 匹配后得到的字符串未匹配返回null
* @since 3.1.2
@ -54,9 +65,9 @@ public class ReUtil {
/**
* 获得匹配的字符串
*
* @param regex 匹配的正则
* @param content 被匹配的内容
*
* @param regex 匹配的正则
* @param content 被匹配的内容
* @param groupIndex 匹配正则的分组序号
* @return 匹配后得到的字符串未匹配返回null
*/
@ -72,7 +83,7 @@ public class ReUtil {
/**
* 获得匹配的字符串获得正则中分组0的内容
*
*
* @param pattern 编译后的正则模式
* @param content 被匹配的内容
* @return 匹配后得到的字符串未匹配返回null
@ -84,7 +95,7 @@ public class ReUtil {
/**
* 获得匹配的字符串获得正则中分组1的内容
*
*
* @param pattern 编译后的正则模式
* @param content 被匹配的内容
* @return 匹配后得到的字符串未匹配返回null
@ -96,9 +107,9 @@ public class ReUtil {
/**
* 获得匹配的字符串对应分组0表示整个匹配内容1表示第一个括号分组内容依次类推
*
* @param pattern 编译后的正则模式
* @param content 被匹配的内容
*
* @param pattern 编译后的正则模式
* @param content 被匹配的内容
* @param groupIndex 匹配正则的分组序号0表示整个匹配内容1表示第一个括号分组内容依次类推
* @return 匹配后得到的字符串未匹配返回null
*/
@ -113,10 +124,10 @@ public class ReUtil {
}
return null;
}
/**
* 获得匹配的字符串匹配到的所有分组
*
*
* @param pattern 编译后的正则模式
* @param content 被匹配的内容
* @return 匹配后得到的字符串数组按照分组顺序依次列出未匹配到返回空列表任何一个参数为null返回null
@ -128,9 +139,9 @@ public class ReUtil {
/**
* 获得匹配的字符串匹配到的所有分组
*
* @param pattern 编译后的正则模式
* @param content 被匹配的内容
*
* @param pattern 编译后的正则模式
* @param content 被匹配的内容
* @param withGroup0 是否包括分组0此分组表示全匹配的信息
* @return 匹配后得到的字符串数组按照分组顺序依次列出未匹配到返回空列表任何一个参数为null返回null
* @since 4.0.13
@ -156,9 +167,9 @@ public class ReUtil {
* 从content中匹配出多个值并根据template生成新的字符串<br>
* 例如<br>
* content 2013年5月 pattern (.*?)(.*?) template $1-$2 return 2013-5
*
* @param pattern 匹配正则
* @param content 被匹配的内容
*
* @param pattern 匹配正则
* @param content 被匹配的内容
* @param template 生成内容模板变量 $1 表示group1的内容以此类推
* @return 新字符串
*/
@ -189,9 +200,9 @@ public class ReUtil {
* 匹配结束后会删除匹配内容之前的内容包括匹配内容<br>
* 例如<br>
* content 2013年5月 pattern (.*?)(.*?) template $1-$2 return 2013-5
*
* @param regex 匹配正则字符串
* @param content 被匹配的内容
*
* @param regex 匹配正则字符串
* @param content 被匹配的内容
* @param template 生成内容模板变量 $1 表示group1的内容以此类推
* @return 按照template拼接后的字符串
*/
@ -210,10 +221,10 @@ public class ReUtil {
* 匹配结束后会删除匹配内容之前的内容包括匹配内容<br>
* 例如<br>
* content 2013年5月 pattern (.*?)(.*?) template $1-$2 return 2013-5
*
* @param pattern 匹配正则
*
* @param pattern 匹配正则
* @param contentHolder 被匹配的内容的Holdervalue为内容正文经过这个方法的原文将被去掉匹配之前的内容
* @param template 生成内容模板变量 $1 表示group1的内容以此类推
* @param template 生成内容模板变量 $1 表示group1的内容以此类推
* @return 新字符串
*/
public static String extractMultiAndDelPre(Pattern pattern, Holder<CharSequence> contentHolder, String template) {
@ -221,7 +232,7 @@ public class ReUtil {
return null;
}
HashSet<String> varNums = findAll(PatternPool.GROUP_VAR, template, 1, new HashSet<String>());
HashSet<String> varNums = findAll(PatternPool.GROUP_VAR, template, 1, new HashSet<>());
final CharSequence content = contentHolder.get();
Matcher matcher = pattern.matcher(content);
@ -240,10 +251,10 @@ public class ReUtil {
* 从content中匹配出多个值并根据template生成新的字符串<br>
* 例如<br>
* content 2013年5月 pattern (.*?)(.*?) template $1-$2 return 2013-5
*
* @param regex 匹配正则字符串
*
* @param regex 匹配正则字符串
* @param contentHolder 被匹配的内容的Holdervalue为内容正文经过这个方法的原文将被去掉匹配之前的内容
* @param template 生成内容模板变量 $1 表示group1的内容以此类推
* @param template 生成内容模板变量 $1 表示group1的内容以此类推
* @return 按照template拼接后的字符串
*/
public static String extractMultiAndDelPre(String regex, Holder<CharSequence> contentHolder, String template) {
@ -258,8 +269,8 @@ public class ReUtil {
/**
* 删除匹配的第一个内容
*
* @param regex 正则
*
* @param regex 正则
* @param content 被匹配的内容
* @return 删除后剩余的内容
*/
@ -275,7 +286,7 @@ public class ReUtil {
/**
* 删除匹配的第一个内容
*
*
* @param pattern 正则
* @param content 被匹配的内容
* @return 删除后剩余的内容
@ -290,8 +301,8 @@ public class ReUtil {
/**
* 删除匹配的全部内容
*
* @param regex 正则
*
* @param regex 正则
* @param content 被匹配的内容
* @return 删除后剩余的内容
*/
@ -307,7 +318,7 @@ public class ReUtil {
/**
* 删除匹配的全部内容
*
*
* @param pattern 正则
* @param content 被匹配的内容
* @return 删除后剩余的内容
@ -322,8 +333,8 @@ public class ReUtil {
/**
* 删除正则匹配到的内容之前的字符 如果没有找到则返回原文
*
* @param regex 定位正则
*
* @param regex 定位正则
* @param content 被查找的内容
* @return 删除前缀后的新内容
*/
@ -343,8 +354,8 @@ public class ReUtil {
/**
* 取得内容中匹配的所有结果获得匹配的所有结果中正则对应分组0的内容
*
* @param regex 正则
*
* @param regex 正则
* @param content 被查找的内容
* @return 结果列表
* @since 3.1.2
@ -355,8 +366,8 @@ public class ReUtil {
/**
* 取得内容中匹配的所有结果获得匹配的所有结果中正则对应分组1的内容
*
* @param regex 正则
*
* @param regex 正则
* @param content 被查找的内容
* @return 结果列表
* @since 3.1.2
@ -367,24 +378,24 @@ public class ReUtil {
/**
* 取得内容中匹配的所有结果
*
* @param regex 正则
*
* @param regex 正则
* @param content 被查找的内容
* @param group 正则的分组
* @param group 正则的分组
* @return 结果列表
* @since 3.0.6
*/
public static List<String> findAll(String regex, CharSequence content, int group) {
return findAll(regex, content, group, new ArrayList<String>());
return findAll(regex, content, group, new ArrayList<>());
}
/**
* 取得内容中匹配的所有结果
*
* @param <T> 集合类型
* @param regex 正则
* @param content 被查找的内容
* @param group 正则的分组
*
* @param <T> 集合类型
* @param regex 正则
* @param content 被查找的内容
* @param group 正则的分组
* @param collection 返回的集合类型
* @return 结果集
*/
@ -398,7 +409,7 @@ public class ReUtil {
/**
* 取得内容中匹配的所有结果获得匹配的所有结果中正则对应分组0的内容
*
*
* @param pattern 编译后的正则模式
* @param content 被查找的内容
* @return 结果列表
@ -410,7 +421,7 @@ public class ReUtil {
/**
* 取得内容中匹配的所有结果获得匹配的所有结果中正则对应分组1的内容
*
*
* @param pattern 编译后的正则模式
* @param content 被查找的内容
* @return 结果列表
@ -422,24 +433,24 @@ public class ReUtil {
/**
* 取得内容中匹配的所有结果
*
*
* @param pattern 编译后的正则模式
* @param content 被查找的内容
* @param group 正则的分组
* @param group 正则的分组
* @return 结果列表
* @since 3.0.6
*/
public static List<String> findAll(Pattern pattern, CharSequence content, int group) {
return findAll(pattern, content, group, new ArrayList<String>());
return findAll(pattern, content, group, new ArrayList<>());
}
/**
* 取得内容中匹配的所有结果
*
* @param <T> 集合类型
* @param pattern 编译后的正则模式
* @param content 被查找的内容
* @param group 正则的分组
*
* @param <T> 集合类型
* @param pattern 编译后的正则模式
* @param content 被查找的内容
* @param group 正则的分组
* @param collection 返回的集合类型
* @return 结果集
*/
@ -461,8 +472,8 @@ public class ReUtil {
/**
* 计算指定字符串中匹配pattern的个数
*
* @param regex 正则表达式
*
* @param regex 正则表达式
* @param content 被查找的内容
* @return 匹配个数
*/
@ -478,7 +489,7 @@ public class ReUtil {
/**
* 计算指定字符串中匹配pattern的个数
*
*
* @param pattern 编译后的正则模式
* @param content 被查找的内容
* @return 匹配个数
@ -499,8 +510,8 @@ public class ReUtil {
/**
* 指定内容中是否有表达式匹配的内容
*
* @param regex 正则表达式
*
* @param regex 正则表达式
* @param content 被查找的内容
* @return 指定内容中是否有表达式匹配的内容
* @since 3.3.1
@ -516,7 +527,7 @@ public class ReUtil {
/**
* 指定内容中是否有表达式匹配的内容
*
*
* @param pattern 编译后的正则模式
* @param content 被查找的内容
* @return 指定内容中是否有表达式匹配的内容
@ -531,7 +542,7 @@ public class ReUtil {
/**
* 从字符串中获得第一个整数
*
*
* @param StringWithNumber 带数字的字符串
* @return 整数
*/
@ -541,8 +552,8 @@ public class ReUtil {
/**
* 给定内容是否匹配正则
*
* @param regex 正则
*
* @param regex 正则
* @param content 内容
* @return 正则为null或者""则不检查返回true内容为null返回false
*/
@ -564,7 +575,7 @@ public class ReUtil {
/**
* 给定内容是否匹配正则
*
*
* @param pattern 模式
* @param content 内容
* @return 正则为null或者""则不检查返回true内容为null返回false
@ -580,18 +591,18 @@ public class ReUtil {
/**
* 正则替换指定值<br>
* 通过正则查找到字符串然后把匹配到的字符串加入到replacementTemplate中$1表示分组1的字符串
*
*
* <p>
* 例如原字符串是中文1234我想把1234换成(1234)则可以
*
*
* <pre>
* ReUtil.replaceAll("中文1234", "(\\d+)", "($1)"))
*
*
* 结果中文(1234)
* </pre>
*
* @param content 文本
* @param regex 正则
*
* @param content 文本
* @param regex 正则
* @param replacementTemplate 替换的文本模板可以使用$1类似的变量提取正则匹配出的内容
* @return 处理后的文本
*/
@ -603,9 +614,9 @@ public class ReUtil {
/**
* 正则替换指定值<br>
* 通过正则查找到字符串然后把匹配到的字符串加入到replacementTemplate中$1表示分组1的字符串
*
* @param content 文本
* @param pattern {@link Pattern}
*
* @param content 文本
* @param pattern {@link Pattern}
* @param replacementTemplate 替换的文本模板可以使用$1类似的变量提取正则匹配出的内容
* @return 处理后的文本
* @since 3.0.4
@ -618,7 +629,7 @@ public class ReUtil {
final Matcher matcher = pattern.matcher(content);
boolean result = matcher.find();
if (result) {
final Set<String> varNums = findAll(PatternPool.GROUP_VAR, replacementTemplate, 1, new HashSet<String>());
final Set<String> varNums = findAll(PatternPool.GROUP_VAR, replacementTemplate, 1, new HashSet<>());
final StringBuffer sb = new StringBuffer();
do {
String replacement = replacementTemplate;
@ -634,12 +645,12 @@ public class ReUtil {
}
return StrUtil.str(content);
}
/**
* 替换所有正则匹配的文本并使用自定义函数决定如何替换
*
* @param str 要替换的字符串
* @param regex 用于匹配的正则式
*
* @param str 要替换的字符串
* @param regex 用于匹配的正则式
* @param replaceFun 决定如何替换的函数
* @return 替换后的文本
* @since 4.2.2
@ -647,17 +658,17 @@ public class ReUtil {
public static String replaceAll(CharSequence str, String regex, Func1<Matcher, String> replaceFun) {
return replaceAll(str, Pattern.compile(regex), replaceFun);
}
/**
* 替换所有正则匹配的文本并使用自定义函数决定如何替换
*
* @param str 要替换的字符串
* @param pattern 用于匹配的正则式
*
* @param str 要替换的字符串
* @param pattern 用于匹配的正则式
* @param replaceFun 决定如何替换的函数,可能被多次调用当有多个匹配时
* @return 替换后的字符串
* @since 4.2.2
*/
public static String replaceAll(CharSequence str, Pattern pattern, Func1<Matcher, String> replaceFun){
public static String replaceAll(CharSequence str, Pattern pattern, Func1<Matcher, String> replaceFun) {
if (StrUtil.isEmpty(str)) {
return StrUtil.str(str);
}
@ -677,7 +688,7 @@ public class ReUtil {
/**
* 转义字符将正则的关键字转义
*
*
* @param c 字符
* @return 转义后的文本
*/
@ -692,7 +703,7 @@ public class ReUtil {
/**
* 转义字符串将正则的关键字转义
*
*
* @param content 文本
* @return 转义后的文本
*/

View File

@ -12,51 +12,50 @@ import java.util.List;
* @author liangbaikai
*/
public class TreeTest {
// 模拟数据
static List<Node> nodeList = CollectionUtil.newArrayList();
// 模拟数据
static List<TreeNode<String>> nodeList = CollectionUtil.newArrayList();
static {
// 模拟数据
nodeList.add(new Node("1", "0", "系统管理", 5));
nodeList.add(new Node("11", "1", "用户管理", 222222));
nodeList.add(new Node("111", "11", "用户添加", 0));
nodeList.add(new Node("2", "0", "店铺管理", 1));
nodeList.add(new Node("21", "2", "商品管理", 44));
nodeList.add(new Node("221", "2", "商品管理2", 2));
}
static {
// 模拟数据
nodeList.add(new TreeNode<>("1", "0", "系统管理", 5));
nodeList.add(new TreeNode<>("11", "1", "用户管理", 222222));
nodeList.add(new TreeNode<>("111", "11", "用户添加", 0));
nodeList.add(new TreeNode<>("2", "0", "店铺管理", 1));
nodeList.add(new TreeNode<>("21", "2", "商品管理", 44));
nodeList.add(new TreeNode<>("221", "2", "商品管理2", 2));
}
@Test
public void sampleTree() {
List<TreeNodeMap> treeNodes = TreeUtil.build(nodeList, "0");
@Test
public void sampleTree() {
List<Tree<String>> treeNodes = TreeUtil.build(nodeList, "0");
for (Tree<String> tree : treeNodes) {
Console.log(tree);
}
}
System.out.println(treeNodes);
}
@Test
public void tree() {
@Test
public void tree() {
//配置
TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
// 自定义属性名 都要默认值的
treeNodeConfig.setWeightKey("order");
treeNodeConfig.setDeep(3);
treeNodeConfig.setIdKey("rid");
//配置
TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
// 自定义属性名 都要默认值的
treeNodeConfig.setWeightKey("order");
treeNodeConfig.setDeep(3);
treeNodeConfig.setIdKey("rid");
//转换器
List<TreeNodeMap> treeNodes = TreeUtil.build(nodeList, "0", treeNodeConfig,
new Convert<Node, TreeNodeMap>() {
@Override
public void convert(Node object, TreeNodeMap treeNode) {
treeNode.setId(object.getId());
treeNode.setParentId(object.getPid());
treeNode.setWeight(object.getWeight());
treeNode.setName(object.getName());
// 扩展属性 ...
treeNode.extra("extraField", 666);
treeNode.extra("other", new Object());
}
});
System.out.println(treeNodes);
}
//转换器
List<Tree<String>> treeNodes = TreeUtil.build(nodeList, "0", treeNodeConfig,
(treeNode, tree) -> {
tree.setId(treeNode.getId());
tree.setParentId(treeNode.getParentId());
tree.setWeight(treeNode.getWeight());
tree.setName(treeNode.getName());
// 扩展属性 ...
tree.putExtra("extraField", 666);
tree.putExtra("other", new Object());
});
System.out.println(treeNodes);
}
}

View File

@ -116,7 +116,7 @@ public class CronUtil {
}
/**
* 移除Task
* 更新Task的执行时间规则
*
* @param id Task的ID
* @param pattern {@link CronPattern}