3. The Nodes API (I)

They see only their own shadows or the shadows of one another, which the fire throws on the opposite wall of the cave

Plato

If you want to master the NetBeans Platform you must master the NetBeans Nodes API.

3.1. What is a Node?

A Node is a visual representation of data. In our application we'll use Nodes to visually represent one or more of our entities, and also to represent collections of entities. So, for instance, we'll have a "QueryNode" to visually represent a "Query" entity. We'll also have a "TweetNode" to visually represent the "Tweet" and "TweetAuthor" entities.

Nodes have a hierarchical relationship between them. A Node has always a set of Children nodes, that can be of different types. In our application the "QueryNode" will have a list of "TweetNode" as its children.

Figure 2. Nodes are visual representation of entities

Nodes are visual representation of entities
(click to enlarge)


Nodes are also used to visually group a subset of entities. We could, for instance, define an "HourRangeNode" that represents a time period (an hour ago, six hours ago and the last day, for instance) and then make these "HourRangeNode"s be the children of our "QueryNode". We could then group our "TweetNodes" as children of hour "HourRangeNode". So the relationship between these nodes would be "QueryNode" > "HourRangeNode" > "TweetNode". We won't be doing this just to keep things simple, though.

Nodes may have an icon, a display name, or a display name in HTML, among many other different things. Nodes allow us to separate the visual representation of an entity from the entity itself. This is important because you may want to have different representations of the same entity, and because you may modify or rearrange your client-side model while keeping the visual aspects intact. Figure Nodes are visual representation of entities explains graphically the relationship between nodes and entities.

3.2. Creating leaf nodes

The easiest way to create a leaf node is by extending the AbstractNode class and passing it the "Children.LEAF" constant on the construtor. Let's see an example:

public class TweetNode
  extends AbstractNode 1
{
  private Tweet tweet;
  public TweetNode( Tweet entity ) 2
  {
    super( Children.LEAF, entity.getLookup() ); 3
    this.tweet = entity;
  }

  @Override
  public String getDisplayName() 4
  {
    return tweet.getTweetAuthor().getName() + ": " + tweet.getText();
  }
}

1

You extend the AbstractNode class. There're other classes you can extend too, but that's out of scope for these notes.

2

You pass the entity (or entities) that you want to visualize in the constructor. You want to keep a reference to them in a node private variable, so you can refer to it to extract visualization information for your entity (or entities).

3

You pass the "Children.LEAF" constant to the super class, indicating that this node has no children at all, and pass the "Lookup" of the node to the constructor of the super class. By doing so all the abilities of the entity are included in the abilities of the node. This is usually handy.

4

You then override any methods you want from the super class. In the example above we override the "getDisplayName" method, which returns a String used to visualize the node. In the example above we include the author name and the text of the tweet.

3.3. Creating a leaf node with abilities

In the example above the TweetNode has all the abilities of the Tweet entity, because we are passing the "Lookup" of the entity to the constructor of the AbstractNode class.

In some cases you may want to have different set of abilities for entities and for nodes, but still combine them within a single lookup. You do this by passing the AbstractNode constructor a ProxyLookup, a special kind of Lookup that combines two or more lookups.

Combining object abilities is a very common and powerful NetBeans Platform idiom, so it deserves an explanation. Let's see an example by improving our previous example:


public class TweetNode
  extends AbstractNode 
{
  private Tweet tweet;
  private InstanceContent instanceContent;

  public TweetNode( Tweet entity ) 
  {
    this( entity, new InstanceContent() ); 1
  }

  private TweetNode( Tweet entity, InstanceContent ic ) 2
  {
    super( Children.LEAF,
        new ProxyLookup( entity.getLookup(), new AbstractLookup(ic) ); 3
    this.tweet = entity;
    this.instanceContent = ic;
    // Add abilities to the  node in this instanceContent
    this.instanceContent.add( new WhateverAbility() ); 4
    // Add the entity to the abilities of the node
    this.instanceContent.add( entity ); 5
  }

2

You create a (usually private) constructor that is passed an entity and an InstanceContent. This InstanceContent is going to keep the abilities of the node, so you want to keep this in a private variable in the node too.

1

In the previous constructor you just call the new constructor, and pass it a brand new InstanceContent.

3

In the new (private) constructor you pass the AbstractNode constructor a ProxyLookup, that combines the lookup of the entity with a new AbsctractLookup with the abilities of the instance content.

4

You then just add the node-specific abilities to the InstanceContent by invoking the "add" method to the instance content. Of course you can remove abilities in the node at any time by invoking the "remove" method on the InstanceContent.

5

A handy trick is to add the entity to the abilities of the node, so that we can easily "lookup" the entity from the node.

3.4. Summary

In this section we've get acquaintanced with the NetBeans Nodes API. We've understood that a Node is a representation of an entity (a folder, a file, a database connection, a Tweet and its TweetAuthor, etc.).

We've also seen that Nodes may have all the abilities of the entities they represent, or may enhance those abilities with their own.

We're now ready to explore more about the Nodes API: we'll see how to create nodes with children. That requires a new section.