Complex Node Hierarchies

本文探讨了使用Nodes API创建复杂的音乐乐队节点层级结构的过程。通过示例展示了如何在层级中同时展示乐队成员和经理,以及如何优化节点结构实现更好的视觉效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Complex Node Hierarchies

When you're using the Nodes API, things can become a bit tricky, especially when you want to visualize different Node types on the same level within a hierarchy.

As an example, let's imagine our domain model represents music bands, e.g., 'Oasis', etc. Here's a very simple node hierarchy that simply lists the names of the bands, i.e., each Node below visualizes an underlying Band object that is defined by a name:

After you've done the above, you want to show the members of the band. So, now the Band object is defined by a name and a list of Members:

However, let's say each Band has a list of Members, as well as one Manager. Here things start getting a bit tricky. The ChildFactory class has a 'createNodesForKey' method that returns multiple Nodes, so your inclination would be to use that, something like this:

@Override
protected Node[] createNodesForKey(Band key) {
    int size = key.getMembers().size() + 1;
    Node[] nodes = new Node[size];
    for (int i = 0; i < key.getMembers().size(); i++) {
        try {
            nodes[i] = new MemberNode(key.getMembers().get(i));
        } catch (IntrospectionException ex) {
            Exceptions.printStackTrace(ex);
        }
    }
    try {
        nodes[size-1] = new ManagerNode(key.getManager());
    } catch (IntrospectionException ex) {
        Exceptions.printStackTrace(ex);
    }
    return nodes;
}

What the above gets you is a structure like this:

However, you're more likely to want the structure below instead, i.e., a container Node for all the Members, together with a leaf Node for the Manager, of which there will always be one:

To achieve the above, I learned this approach from Sven Reimers during JCrete:

public class MusicBandContainerChildFactory extends 
    ChildFactory<MusicBandContainerChildFactory.Container> {
    
    private final Band band;

    public enum Container {
        MEMBERS, MANAGER
    }
    
    public MusicBandContainerChildFactory(Band band) {
        this.band = band;
    }

    @Override
    protected boolean createKeys(List<Container> list) {
        list.add(Container.MEMBERS);
        list.add(Container.MANAGER);
        return true;
    }
    
    @Override
    protected Node createNodeForKey(Container key) {
        switch (key) {
            case MEMBERS: {
                try {
                    return new MemberContainerNode(key);
                } catch (IntrospectionException ex) {
                    Exceptions.printStackTrace(ex);
                }
            }
            case MANAGER: {
                try {
                    return new ManagerNode(key);
                } catch (IntrospectionException ex) {
                    Exceptions.printStackTrace(ex);
                }
            }
        }
        return null;
    }
    
    private class ManagerNode extends BeanNode {
        public ManagerNode(Container key) throws IntrospectionException {
            super(key);
            setDisplayName("Manager: "+band.getManager().getName());
        }
    }
    
    private class MemberContainerNode extends BeanNode {
        public MemberContainerNode(Container key) throws IntrospectionException {
            super(key, Children.create(new BandMemberChildFactory(band), true));
            setDisplayName("Members");
        }
    }
    
    private class BandMemberChildFactory extends ChildFactory<Member> {
        private final Band bean;
        public BandMemberChildFactory(Band bean) {
            this.bean = bean;
        }
        @Override
        protected boolean createKeys(List<Member> list) {
            list.addAll(bean.getMembers());
            return true;
        }
        @Override
        protected Node createNodeForKey(Member key) {
            BandMemberNode node = null;
            try {
                node = new BandMemberNode(key);
            } catch (IntrospectionException ex) {
                Exceptions.printStackTrace(ex);
            }
            return node;
        }
    }

    private class BandMemberNode extends BeanNode<Member> {
        public BandMemberNode(Member bean) throws IntrospectionException {
            super(bean, Children.LEAF);
            setDisplayName(bean.getName());
        }
    }
    
}

Thanks, Sven!

#include <iostream> #include <vector> #include <string> #include <sstream> #include <cstdlib> using namespace std; struct TypeNode { string type_name; TypeNode* first = NULL; TypeNode* second = NULL; TypeNode(string t) : type_name(t), first(NULL), second(NULL) {} }; vector<TypeNode*> vars; vector<string> var_names; void addVariable(const string& name, const string& type_str) { if (type_str == "int" || type_str == "double") { vars.push_back(new TypeNode(type_str)); } else { size_t pos = type_str.find("<"); string base_type = type_str.substr(0, pos); if (base_type != "pair") { cerr << "Error: Invalid type definition!" << endl; exit(-1); } string inner_types = type_str.substr(pos + 1, type_str.size() - pos - 2); stringstream ss(inner_types); string left_type, right_type; getline(ss, left_type, ','); getline(ss, right_type); TypeNode* node = new TypeNode("pair"); node->first = new TypeNode(left_type); node->second = new TypeNode(right_type); vars.push_back(node); } var_names.push_back(name); } TypeNode* getTypeNodeByName(const string& name) { for (size_t i = 0; i < var_names.size(); ++i) { if (var_names[i] == name) return vars[i]; } return NULL; } string queryType(TypeNode* root, const vector<string>& steps) { if (!root) return ""; if (steps.empty()) return root->type_name; string step = steps.front(); if (step == "first" && root->first) { return queryType(root->first, vector<string>(steps.begin() + 1, steps.end())); } else if (step == "second" && root->second) { return queryType(root->second, vector<string>(steps.begin() + 1, steps.end())); } else { return ""; } } string resolveQuery(const string& query_str) { size_t dot_pos = query_str.find('.'); string var_name = query_str.substr(0, dot_pos); string rest = query_str.substr(dot_pos + 1); TypeNode* root = getTypeNodeByName(var_name); if (!root) return ""; vector<string> steps; while (!rest.empty()) { size_t next_dot = rest.find('.'); string current_step = rest.substr(0, next_dot); steps.push_back(current_step); if (next_dot == string::npos) break; rest = rest.substr(next_dot + 1); } return queryType(root, steps); } int main() { int n, q; cin >> n >> q; cin.ignore(); for (int i = 0; i < n; ++i) { string line; getline(cin, line); size_t space_pos = line.find(' '); string type_def = line.substr(0, space_pos); string var_name = line.substr(space_pos + 1); if (!var_name.empty() && var_name[var_name.size()-1] == ';') { var_name.erase(var_name.size()-1); } addVariable(var_name, type_def); } for (int i = 0; i < q; ++i) { string query; cin >> query; cout << resolveQuery(query) << endl; } return 0; }样例输入 #1 5 5 int a1; double a2; pair<int,int> pr; pair<pair<int,int>,pair<int,int>> BBKKBKK; pair<int,pair<pair<double,int>,double>> __frost_ice; a1 a2 pr.first BBKKBKK __frost_ice.second.first.second 错误输出: int 期望输出: int double int pair<pair<int,int>,pair<int,int>> int
03-31
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值