X

Geertjan's Blog

  • July 22, 2015

Complex Node Hierarchies

Geertjan Wielenga
Product Manager

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!

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.