All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jrouter.impl.PathTree Maven / Gradle / Ivy

/*
 * Copyright (C) 2010-2111 [email protected]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package net.jrouter.impl;

import lombok.Getter;

import java.io.Serializable;
import java.util.*;

/**
 * 适配{@code Map}接口的树结构路径。包含了一个树结构路径和一个含相关联值的最终路径的{@code Set}集合。
 *
 * @param  与路径相关联值的类型。
 */
class PathTreeMap extends AbstractMap implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 树结构路径
     */
    private final PathTree tree;

    /**
     * entrySet views for adapting for the Map interface
     */
    private transient Set> entrySet = null;

    /**
     * 构造一个指定路径分割符的映射路径和关联值的映射。
     * @param separator 指定的路径分割符。
     */
    public PathTreeMap(char separator) {
        super();
        tree = new PathTree<>(separator);
        entrySet = new HashSet<>();
    }

    /**
     * 添加指定的全路径与其相关联的值至视图。
     * @param fullPath 指定的全路径。
     * @param value 与路径相关联的值。
     */
    private void addEntrySet(String fullPath, V value) {
        entrySet.add(new SimpleImmutableEntry<>(fullPath, value));
    }

    @Override
    public V remove(Object key) {
        throw new UnsupportedOperationException();
    }

    @Override
    public V put(String fullPath, V value) {
        V res = tree.put(fullPath, value);
        // if add new to addEntrySet
        if (res == null) {
            addEntrySet(fullPath, value);
        }
        return res;
    }

    /**
     * @see PathTree#get(String, Map)
     */
    public V get(String fullPath, Map matchParameters) {
        return tree.get(fullPath, matchParameters);
    }

    @Override
    public V get(Object fullPath) {
        return tree.get(fullPath.toString(), null);
    }

    @Override
    public void clear() {
        entrySet.clear();
        tree.clear();
    }

    @Override
    public Set> entrySet() {
        return entrySet;
    }

}

/**
 * 树形结构存储的映射路径与其关联值。
 *
 * @param  与路径相关联值的类型。
 */
class PathTree implements Serializable {

    /**
     * 默认的路径分隔符
     */
    public static final char PATH_SEPARATOR = '/';

    /**
     * 单路径匹配标识
     */
    public static final String SINGLE_MATCH = "*";

    private static final long serialVersionUID = 1L;

    /**
     * 路径尾匹配
     * @deprecated
     */
    @Deprecated
    private static final String LAST_MATCH = "**";

    /**
     * 路径分割符
     */
    @Getter
    private final char pathSeparator;

    /**
     * 树路径的根节点
     */
    private TreeNode root;

    /**
     * 构造一个默认路径分割符'/'的路径树。
     */
    PathTree() {
        this(PATH_SEPARATOR);
    }

    /**
     * 构造一个指定路径分割符的路径树。
     * @param pathSeparator 指定的路径分割符。
     */
    PathTree(char pathSeparator) {
        this.pathSeparator = pathSeparator;
        root = new TreeNode<>(Character.toString(pathSeparator), null);
        root.code = 1;
    }

    /**
     * 获取添加路径的非完全匹配名称,完全字符串则返回 null。
     * 

* * --> null * * --> null * abc --> null * {} --> null * {abc} --> abc * { abc } --> abc (" abc ") * {*} --> * * { } --> (" ") * {{abc} --> {abc * {{abc}}} --> {abc}} * * @param path 路径字符串。 * @return 路径的非完全匹配名称,完全字符串则返回 null。 */ private static String getMatchKey(String path) { int len = path.length(); // at least 2 chars if (len <= 2) { return null; } int begin = -1; int end = -1; for (int i = 0; i < len; i++) { char c = path.charAt(i); if (begin == -1 && (c == '{' || c == '[' || c == '(')) { begin = i; } else if (c == '}' || c == ']' || c == ')') { end = i; } } // return the no trimmed string between the first '{[(' and the last '}])'. return begin != -1 && end - begin > 1 ? path.substring(begin + 1, end) : null; } /** * 判断是否是一个键匹配的路径。 * @param path 指定路径。 * @return 路径包含键匹配返回true,否则返回false。 * @see #getMatchKey(java.lang.String) */ private static boolean isMatchKay(String path) { int len = path.length(); // at least 2 chars if (len <= 2) { return false; } int begin = -1; int end = -1; for (int i = 0; i < len; i++) { char c = path.charAt(i); if (begin == -1 && (c == '{' || c == '[' || c == '(')) { begin = i; } else if (c == '}' || c == ']' || c == ')') { end = i; } } return begin != -1 && end - begin > 1; } /** * 判断是否为根路径。 * @param fullPath 指定的全路径。 * @return 是否为根路径。 */ private boolean isRoot(String fullPath) { return fullPath.length() == 1 && pathSeparator == fullPath.charAt(0); } /** * 添加路径与其相关联的值,并返回原有路径的值; 如果原有路径已存在值则保留并返回该值,不存在则返回 null。 * @param fullPath 指定的相关路径。 * @param value 与路径相关联的值。 * @return 以前与路径相关联的值,如果没有则返回 null。 * @throws NullPointerException 如果路径相关联的值为 null。 */ public V put(String fullPath, V value) { if (value == null) { throw new NullPointerException();// NOPMD } // root path if (isRoot(fullPath)) { V oldRoot = root.value; root.value = value; return oldRoot; } final String[] paths = parsePath(fullPath); int len = paths.length; if (len == 0) { throw new IllegalArgumentException("Null path : " + fullPath); } TreeNode cur = root; for (int i = 0; i < len - 1; i++) { // add tree branches cur = cur.addBranch(paths, paths[i]); // cur = cur.get(paths[i]); } // add the last path with value. // return null means to add a new node, else return old node with value. return cur.addLeaf(paths, paths[len - 1], value); } /** * 获取指定路径相关联的值;如果不包含该路径的关联关系,则返回 null。 * @param fullPath 指定路径的名称。 * @return 指定路径相关联的值;如果不包含该路径的关联关系,则返回 null。 */ public V get(String fullPath) { return get(fullPath, null); } /** * 获取指定路径相关联的值;如果不包含该路径的关联关系,则返回 null。 * @param fullPath 指定路径的名称。 * @param matchParameters 路径中匹配的键值映射。 * @return 指定路径相关联的值;如果不包含该路径的关联关系,则返回 null。 */ public V get(String fullPath, Map matchParameters) { // root path if (isRoot(fullPath)) { return root.value; } String[] paths = parsePath(fullPath); int len = paths.length; if (len == 0) { return null; } // the current nodes as parents List> current = new ArrayList<>(1); // the next all children nodes List> next = new ArrayList<>(5); // 初始化当前的节点集合指向根节点 current.add(root); // 遍历树 for (int i = 0; i < len - 1; i++) { for (TreeNode tn : current) { if (tn.children == null || tn.children.isEmpty()) { continue; } TreeNode match = tn.children.get(paths[i]); if (match != null) { next.add(match); } match = tn.children.get(SINGLE_MATCH); if (match != null) { next.add(match); } } // System.out.println("Next : " + next + ", Current : " + current); if (next.isEmpty()) { // System.out.println("Not Found For [" + fullPath + "]"); // not find return null; } // change the current to the next then clear the next for reuse List> temp = current; current = next; next = temp; next.clear(); } // 找寻最终路径有值的节点 for (TreeNode tn : current) { if (tn.children == null || tn.children.isEmpty()) { continue; } TreeNode match = tn.children.get(paths[len - 1]); if (match != null && match.value != null) { next.add(match); } match = tn.children.get(SINGLE_MATCH); if (match != null && match.value != null) { next.add(match); } } if (next.isEmpty()) { // System.out.println("Not Found For [" + fullPath + "]"); // not find return null; } current.clear(); current = next; // System.out.println("Final Match Nodes List : " + current); // 最终匹配的路径节点 TreeNode finalMatcher = null; // the final list must have at least on value if (current.size() == 1) { finalMatcher = current.get(0); fillMatchParameters(finalMatcher, paths, matchParameters); return finalMatcher.value; } // compare the paths to find the most matched one which has the maximum code. int max = current.get(0).code; int index = 0; int size = current.size(); for (int i = 1; i < size; i++) { TreeNode tr = current.get(i); if (tr.code > max) { max = tr.code; index = i; } } // System.out.println("Index & Max : " + index + "," + max); // get the final match node finalMatcher = current.get(index); current.clear(); // fill the MatchParameters fillMatchParameters(finalMatcher, paths, matchParameters); return finalMatcher.value; } /* * 将路径数组按照指定的路径码(二进制标识)填充进链表。 */ private void fillMatchParameters(TreeNode matcher, String[] paths, Map matchParameters) { if (matchParameters != null) { /* * //parse the * parameters String fullCode = * Integer.toBinaryString(matcher.code); //0 is the root '/' for (int i = 1; i * < fullCode.length(); i++) { //if 0 means '*' if (fullCode.charAt(i) == '0') * { matchParameters.put(i - 1 + "", paths[i - 1]); } } * */ IndexKey[] iks = matcher.indexKeys; if (iks != null) { for (IndexKey ik : iks) { // 不做索引下标映射 // matchParameters.put(i + "", paths[ik.index]); // 不做匹配路径键值的重复判断 matchParameters.put(ik.matchKey, paths[ik.index]); } } } // System.out.println("Get Final matchParameters : " + matchParameters); } /** * 将全路径解析成字符串数组,排除了"连续分割符"。 * @param fullPath 全路径字符串。 * @return 解析后的事字符串数组。 */ private String[] parsePath(String fullPath) { List list = new ArrayList<>(5); // fullPath is trimmed int last = -1; int cur = -1; int len = fullPath.length(); for (int i = 0; i < len; i++) { if (pathSeparator == fullPath.charAt(i)) { cur = i; if (cur > last) { // avoid ...///... if (cur - last > 1) { list.add(fullPath.substring(last + 1, cur)); } last = cur; } } } if (cur < len - 1) { cur = len; list.add(fullPath.substring(last + 1, cur)); } return list.toArray(new String[0]); } /** * 清除整个树结构的所有路径与其相关联值的映射关系。 */ public void clear() { root = null; } /** * 节点路径,记载了节点的路径、路径代码、相关联的值、子路径等信息。 */ private static class TreeNode implements Serializable { private static final long serialVersionUID = 1L; /** * 节点的相对路径,可能重复不唯一(至根节点的绝对路径唯一) */ private String path; /** * 根节点到此节点的路径代码(二进制)。根节点代码为1。 0代表匹配,1代表确切字符串;多匹配路径取代码最大的为最匹配路径。 */ private int code = -1; /** * 节点路径相关联的值 */ private V value; /** * 节点路径的匹配索引及键名数组,不含相关联值的节点为 null */ private IndexKey[] indexKeys; /** * 节点路径的子路径。叶子节点无子路径,且一定包含相关联的值 */ private Map> children; /** * 构造一个指定相对路径和相关联值的节点路径。 * @param path 相对与父节点的路径。 * @param value 与节点相关联的值。 */ private TreeNode(String path, V value) { this.path = path; this.value = value; } /* * 获取子路径的节点。 * * @param path 指定的子路径名称。 * * @return 子路径的节点,如果没有则返回null。 */ // public TreeNode get(String path) { // return children == null ? null : children.get(path); // } /** * 在此节点上添加子路径。 如果原有子路径节点已存在则返回原有的节点,否则添加并返回新增的子节点。 * @param paths 子节点全路径解析后的路径字符串数组。 * @param child 指定的子路径名称。 * @return 新增子路径的节点;如果原子路径节点存在则返回原子路径节点。 */ private TreeNode addBranch(final String[] paths, String child) { // 未创建子路径节点集合 if (children == null) { children = new HashMap<>(); TreeNode newNode = new TreeNode<>(child, null); // set the new child node and put it in the children nodes setChildNode(newNode, paths); children.put(newNode.path, newNode); // 返回新增的节点 return newNode; } // 查找子节点路径,如果路径包含键匹配,则查找路径为'*' TreeNode old = children.get(isMatchKay(child) ? SINGLE_MATCH : child); // 子节点集合未包含此节点 if (old == null) { TreeNode newNode = new TreeNode<>(child, null); // set the new child node and put it in the children nodes setChildNode(newNode, paths); children.put(newNode.path, newNode); // 返回新增的节点 return newNode; } else { // 返回已存在的节点 return old; } } /** * 添加叶子节点的路径与其相关联的值。 如果原叶子节点路径已存在值则返回其原有的值,否则返回 null。 * @param paths 子节点全路径解析后的路径字符串数组。 * @param child 指定的子路径名称。 * @param value 叶子节点路径相关联的值。 * @return 如果原叶子节点的值存在则返回原叶子节点的值,否则返回 null。 */ private V addLeaf(final String[] paths, String child, V value) { // 未创建子路径节点集合 if (children == null) { children = new HashMap<>(); TreeNode newNode = new TreeNode<>(child, value); // set the new child node and put it in the children nodes setChildNode(newNode, paths); children.put(newNode.path, newNode); // 新增叶子节点返回 null return null; } // 查找子节点路径,如果路径包含键匹配,则查找路径为'*' TreeNode old = children.get(isMatchKay(child) ? SINGLE_MATCH : child); // 子节点集合未包含此节点 if (old == null) { TreeNode newNode = new TreeNode<>(child, value); // set the new child node and put it in the children nodes setChildNode(newNode, paths); children.put(newNode.path, newNode); // 新增叶子节点返回 null return null; } /* * 覆盖原节点的值,并返回原有的值,如果原节点没有值返回 null */ V oldValue = old.value; old.value = value; // 重新设置索引/索引/值数组 setLeafIndexKeys(old, paths); return oldValue; } /** * 设置此节点下子节点路径的代码、索引/值类数组等信息。 如果子节点路径包含键匹配,则将其路径名称设为'*'用于路径的查询。 * @param child 子节点。 * @param paths 子节点全路径解析后的路径字符串数组。 */ private void setChildNode(TreeNode child, String[] paths) { String childPath = child.path; // if child path is '*' child.code = (code << 1); if (child.code < 0) { throw new IllegalArgumentException("Depth of the tree is too large, no more than 32 layers."); } // not equals '*' if (!SINGLE_MATCH.equals(childPath)) { // 结果代表是否完全匹配的 String key = getMatchKey(childPath); // 返回 null代表完全匹配的字符串 if (key == null) { // 为确切的字符串则此路径(二进制)末位为1 child.code += 1; } else { // 如果路径包含键匹配,则设置路径为'*',方便添加节点时遍历查询。 child.path = SINGLE_MATCH; } } // 如果为叶子节点,设置其索引/值数组 setLeafIndexKeys(child, paths); } /** * 设置叶子节点的索引/值数组。 并处理连续'*'路径(如果有)为*,*1,*2... * @param leaf 叶子节点。 * @param paths 节点全路径解析后的路径字符串数组。 */ private void setLeafIndexKeys(TreeNode leaf, String[] paths) { // 仅叶子节点有相关联的值才添加索引/值数组 if (leaf.value != null) { List keys = new ArrayList<>(paths.length); byte matchIndex = 1; for (byte i = 0; i < paths.length; i++) { if (SINGLE_MATCH.equals(paths[i])) { // *,*1,*2... keys.add(new IndexKey(i, matchIndex == 1 ? SINGLE_MATCH : SINGLE_MATCH + matchIndex)); matchIndex++; } else { // 路径的非完全匹配名称 String key = getMatchKey(paths[i]); if (key != null) { keys.add(new IndexKey(i, key)); } } } // System.out.println("IndexKey : " + keys); if (!keys.isEmpty()) { leaf.indexKeys = keys.toArray(new IndexKey[0]); } // System.out.println("setChildNode : " + child); } } @Override public String toString() { return "TreeNode{" + "path=" + path + ", code=" + Integer.toBinaryString(code) + ", value=" + value + ", children=" + children + '}'; } } /** * 索引/值类。记录了节点匹配符在路径数组中的索引位置及匹配的键名。 */ private static class IndexKey implements Serializable { private static final long serialVersionUID = 1L; /** * Path array index, no more than 32. */ private byte index = -1; /** * 匹配的键名。 */ private final String matchKey; /** * 构造一个指定索引号和键名的类。 * @param index 指定的索引号。 * @param matchKey 指定的键名。 */ public IndexKey(byte index, String matchKey) { this.index = index; this.matchKey = matchKey; } @Override public String toString() { return "IndexKey{" + "index=" + index + ", matchKey=" + matchKey + '}'; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy