Geertjan's Blog

  • September 3, 2010

Constructing Nodes from the Layer and from a Database

Geertjan Wielenga
Product Manager
Now here's a pretty cool piece of code.

Let's imagine you're populating a node hierarchy from a database. In addition, you want to populate the same node hierarchy from the filesystem because you're using @ConvertAsJavaBean to serialize a new business object whenever the user steps through a wizard to create a new customer. At that point, i.e., when Finish is clicked, the .settings file is created in the userdir because maybe the user doesn't want to save the new object as soon as it is created.

So now you need a way to visually distinguish between nodes built from objects in the database versus nodes built from objects in the layer.

Here's how, thanks to Ernest from the course in Stellenbosch. (As the trainer, I set the task but couldn't completely figure out the answer myself.) Look at the code below. We listen for new objects in the layer, at which point we refresh the node hierarchy, i.e., refresh calls createKeys. In createNodeForKey, a FilterNode is used to add an asterisk to the display name of the node, if the underlying object is found in the result object (i.e., in that case, it doesn't come from the database):

public class BusAcctChildFactory extends ChildFactory< BusAcct>  implements LookupListener {
private final Result< BusAcct> result;
private InstanceContent ic;
public BusAcctChildFactory() {//Here we're listening to a folder in the layer
//for new .settings files, which are
//serialized from 'BusAcct' business object,
//via @ConvertAsJavaBean:

result = Lookups.forPath("BusinessAccounts").lookupResult(BusAcct.class);
resultChanged(new LookupEvent(result));
public void resultChanged(LookupEvent ev) {
protected boolean createKeys(List< BusAcct> toPopulate) {//Populate the list from the db:
EntityManager em = Persistence.createEntityManagerFactory("AMSWinJPU").
Query query = em.createQuery("SELECT c FROM BusAcct c");
List< BusAcct> resultList = query.getResultList();
for (BusAcct busAcct : resultList) {
}//Populate the list from the layer:
Collection< ? extends BusAcct> busAcctsFromFS = result.allInstances();
for (BusAcct busAcct : busAcctsFromFS) {
return true;
protected Node createNodeForKey(BusAcct key) {//Create new InstanceContent per Node:
ic = new InstanceContent();
BusAcctNode node = null;
try {
node = new BusAcctNode(ic);//if the key is from the layer,
//which we can check by seeing
//if the current object is in the result,
//create a FilterNode and mark the
//display name of the
//object from the layer as not being
//saved in the database:

if (result.allInstances().contains(key)) {
return new FilterNode(node) {
public String getDisplayName() {
return "\*" + super.getDisplayName();
} catch (IntrospectionException ex) {
return node;

An even better solution, also by Ernest. Create a property for the node, for the "dirty" asterisk:

private class DirtyProperty extends PropertySupport.ReadOnly<String>  {
private final BusAcct c;
private DirtyProperty(BusAcct c) {
super("dirty", String.class, "Dirty", "Property marking as dirty");
this.c = c;
public String getValue() throws IllegalAccessException, InvocationTargetException {
Collection<? extends BusAcct> allBusAcctsInLayer = Lookups.forPath("BusinessAccounts").lookupAll(BusAcct.class);
if (allBusAcctsInLayer.contains(c)) {
return "\*";
} else {
return "";

Then define the OutlineView as follows, using the name "dirty" to link it to the property above:

OutlineView ov = new OutlineView();
ov.setPropertyColumns(new String[]{
"dirty", "",
"baAmsZo", "ZO#",
"baCompany", "Company",
"nameFirst", "First Name",
"nameLast", "Last Name",});
add(ov, BorderLayout.CENTER);

A much cleaner solution, the previous code for checking whether the layer folder contains the current object can be removed. Now look at the OutlineView below, with the "dirty" asterisk shown in the first column (hidden node column!) only for those objects that come from the layer:

Thanks Ernest! We'll be getting more tips and tricks from him on NetBeans Zone soon.

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.