eclipse - common navigator framework (CNF)

CNF is part of the eclipse API that provides a general navigation framework (a tree object) with content that can be provided through eclipse plugin.xml extensions. The hard part though is understanding how to get certain types of content to be linked (bound) to specific content and label providers. For simple hierarchies, this is not a big deal, but for complex hierarchies where the content is spread across plugins and navigation content is provided through multiple specifications of org.eclipse.ui.navigator.navigatorContent extensions.

The tricky part is the triggerPoints and possibleChildren specifications. When is each used? Should you always duplicate content into both of these sections. The enablement element is actually provided so you can do exactly that.

The only true answer comes from looking at the source code. Since content extensions are provided through plugin.xml specifications and navigatorContent components are lazily instantiated, the underlying CommonViewer uses a content service that aggregates the plugin.xml specifications and instantiates them when needed. This is alot of book keeping but the code does this. The other actual navigatorContent sections are kept as descriptors until instantiated (as is common terminology and design in eclipse for lazily instantiated components).

The instantiated classes, once the providers are instantiated are fronted by 2 classes, NavigatorContentServiceContentProvider and NavigatorContentServiceLabelProvider. They implement ITreeContentProvider and ILabelProvider respectively (among other related interfaces e.g. IColorProvider). These classes lazily look for an extension specification that can satisfy its content request. So NavigatorContentServiceContentProvder asks the basic NavigatorContentService for extensions that can handle a specific element. The plugin.xml specification defines triggerPoints and PossibleChildren clauses that allow you to specify when a content provider should be "activated" for a specific element.

Content Providing: If you look at the code for content providing:

public synchronized Object[] getElements(Object anInputElement) {
Set rootContentExtensions = contentService

you see that the first thing the method does is ask the content service for extensions which can handle the specific input element. The method then iterates over all of the extensions asking each to provide elements. The union of these elements is returned. If some extension override others (through the override element in the plugin.xml specification) then those overriding extensions are used to generate elements. This story is also applicable for getChildren(). By looking at the code we see that both getElements() and getChildren() both utilize the triggerPoints specification. For getElements(), you can specify isRoot in a viewerContentBinding:contentExtension specification to force some content to be queried for root elements (the topmost level of the tree).

Note that there is a ICommonNavigatorContentProvider content provider interface that extends ITreeContentProvider with some initialization interfaces so you can access the plugin.xml specification and obtain your own configuration data of some sort.

For label providing, this is a bit different. The getText() method uses

public String getText(Object anElement) {
Set contentExtensions =

which uses the possibleChildren specifications.

Another area of concern is when do the label and content providers become instantiated that are specified in the navigator content sections? Remember, you often specify 2 classes, one for providing content and the other for providing labels. If you combine these two into the same class as I often do, beware that the inputChanged() method is only called when the content part of the class is created NOT when the label provider is created! So if your combined class relies on inputChanged() to provide labels, you have lifecycle timing issues. Beware!


Popular posts from this blog

zio layers and framework integration

typescript and react types

dotty+scala.js+async: interesting options