Freitag, 1. Mai 2009

Common Navigator Framework (CNF)

What is this document?

This document wants to be an effective starting point for all Eclipse-RCP-developers new to the CNF.
It provides you with a brief introduction of what it is and what it is useful for.
It provides some sample code that could be found here. The aim of the sample code is to concentrate on how to use the CNF in RCP-Applications and how to contribute content to one CNF-based view from different plug-ins. All of the sample code sticks to the very basics and should only give you the core-idea of why it could be useful for you.
For everything more advanced you should take a look on the further reading section, the sample code section and the real life example section.

What is the CNF?

In short: It is a view containing a special treeviewer that could be used in Eclipse based applications. The treeviewers degree of details is dynamically configurable via extensions. So one plug-in could create the initial view and a lot of other plugins can change the details that should be displayed.
For example it solves the view-explosion-problem: A view is existing and displays some data. Now you find it usable to view some other details for the already displayed objects. Without the CNF you need to create a new view that duplicates the already visible data appended with the details you need. That “duplication” is at least needed, when the original code of the view could not be changed by you. Let’s say because the view is contributed by another plug-in, from another company.
For more details take a look at the section “further reading”.

A real life example – the Project Explorer

The maybe most famous example is the Project Explorer View within the Eclipse IDE. You can open it’s plugin.xml easily due the "Plug-ins" View with a double-click on org.eclipse.ui.navigator.resources.


Package Explorer View and Project Explorer View

Another example is the Eclipse Web Tools Platform (WTP).
Hints:
  • Some Contributions to the Project Explorer View can be found in the plugin.xml of the Plug-in org.eclipse.jdt.ui .
  • The definition of the Resources Perspective (which the Project Explorer belongs to) could be found in the Plug-in org.eclipse.ui.ide.application.
Hints to prevent confusion:
  • The Package Explorer View within Eclipse is not based on the CNF, although some people name it as an example for the CNF.
  • The Project Explorer View is based on the CNF and provides nearly the same features as the Package Explorer View does. It seems that the Project Explorer View was intended to replace the Package Explorer View someday, but for now there are still pieces of code that only relay on the Package Explorer…

Short history of CNF

  • Created by Michael Elder
  • Originally created for IBM Rational Application Developer (RAD)
  • CNF is new with Eclipse 3.2

My cnfdemo sample code

Tested with: Eclipse 3.4, Eclipse 3.5
Is available for download here
The aim of the sample code is to concentrate on how to use the CNF in RCP-Applications and how to contribute content to one CNF-based view from different plug-ins. All of the sample code sticks to the very basics and should only give you the core-idea of why it could be useful for you.
The cnfdemo consists of 4 very small plugin-projects that you could easily import with the Import-Wizard of the Eclipse IDE. (File - Import - Existing Projects into Workspace)
Hint:
  • The CNF-Plug-in (org.eclipse.ui.navigator) is not included in the RCP Target Platform. If you are just using the Eclipse installation as your Target Platform (which is the normal setup) you don’t need to care about that detail.
The screenshots above give you an overview of which plug-in contributes what to the ui.

cnfdemo: basisc plugin structure part 1

cnfdemo: basisc plugin structure part 2

cnfdemo.core – contains the application and the workbench. More interesting for CNF it also contains the plugin.xml configuration for the CNF-View. It also contains an interface that all Elements of the CNF-treeview should implement.
cnfdemo.fruits – contains model-classes for fruits (bananas and apples). Via plugin.xml this plugin contributes to the CNF-based view and makes the new business-objects visible in the CNF-view. It contains a LabelProvider and a ContentProvider for the fruits.
cnfdemo.geometric – same like cnfdemo.fruits just with some completely different business-objects (geometric forms).
cnfdemo.details – adds a detailed LabelProvider and ContentProvider that makes some details visible for the already displayed business-objects. For apples it shows the weight, for rectangls it shows the length of the sides a and b.

snippets: cnfdemo.core

plugin.xml (cnfdemo.core)
   
      
      
   
   
      
         
            
            
         
      
   
   
      
      
      
         
            
            
         
      
   


NavigatorRoot.java
public class NavigatorRoot extends PlatformObject {
 
	private List cnfTreeObjects = new LinkedList();
 
	public NavigatorRoot(){
 
		cnfTreeObjects.add(new ICnfTreeObject(){
 
			@Override
			public ICnfTreeObject getParent() {
				return null;
			}
 
			@Override
			public String getText() {
				return "dummy - ICnfTreeObject";
			}
		});
	}
 
	public void addCnfTreeObject(ICnfTreeObject cnfTreeObject){
		cnfTreeObjects.add(cnfTreeObject);
	}
 
	public List getCnfTreeObjects() {
		return cnfTreeObjects;
	}
 
	public void addgetCnfTreeObject(ICnfTreeObject cnfTreeObject) {
		cnfTreeObjects.add(cnfTreeObject);
	}
 
}


ICnfTreeObject.java
public interface ICnfTreeObject {
 
	ICnfTreeObject getParent();
	String getText();
 
}


ParentBeanContentProvider.java
public class ParentBeanContentProvider implements ITreeContentProvider {
 
	public Object[] getChildren(Object parentElement) {
		if (parentElement instanceof NavigatorRoot) {
			return ((NavigatorRoot) parentElement).getCnfTreeObjects().toArray();
		}
		return new Object[0];
	}
 
	public Object getParent(Object element) {
		return null;
	}
 
	public boolean hasChildren(Object element) {
		return this.getChildren(element).length > 0;
	}
 
	public Object[] getElements(Object inputElement) {
		return this.getChildren(inputElement);
	}
 
	public void dispose() {
	}
 
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
	}
}


ParentBeanLabelProvider.java
public class ParentBeanLabelProvider implements ILabelProvider {
 
	public Image getImage(Object element) {
		return null;
	}
 
	public String getText(Object element) {
		if (element instanceof ICnfTreeObject) {
			return ((ICnfTreeObject) element).getText();
		}
		return new String();
	}
 
	public void addListener(ILabelProviderListener listener) {
	}
 
	public void dispose() {
	}
 
	public boolean isLabelProperty(Object element, String property) {
		return false;
	}
 
	public void removeListener(ILabelProviderListener listener) {
	}
}

snippets: cnfdemo.detail

plugin.xml (cnfdemo.detail)


   
      
         
            
               
               
               
               
               
               
               
               
            
         ldren>
      
   
   
      
         
            
            
         
      
   



DetailContentProvider.java
public class DetailContentProvider implements ITreeContentProvider {
 
	public Object[] getChildren(Object parentElement) {
		if (parentElement instanceof Rectangle){
			LinkedList linkedList = new LinkedList();
			linkedList.add(new Detail((ICnfTreeObject) parentElement, "side a: " + new Integer(((Rectangle)parentElement).getA()).toString()));
			linkedList.add(new Detail((ICnfTreeObject) parentElement, "side b: " + new Integer(((Rectangle)parentElement).getB()).toString()));
			return linkedList.toArray();
		}
		if (parentElement instanceof Circle){
			LinkedList linkedList = new LinkedList();
			linkedList.add(new Detail((ICnfTreeObject) parentElement, "radius: " + new Integer(((Circle)parentElement).getRadius()).toString()));
			return linkedList.toArray();
		}
		if (parentElement instanceof Apple){
			LinkedList linkedList = new LinkedList();
			linkedList.add(new Detail((ICnfTreeObject) parentElement, "weight: " + new Integer(((Apple)parentElement).getWeight()).toString()));
			return linkedList.toArray();
		}
		if (parentElement instanceof Banana){
			LinkedList linkedList = new LinkedList();
			linkedList.add(new Detail((ICnfTreeObject) parentElement, "weight: " + new Integer(((Banana)parentElement).getWeight()).toString()));
			return linkedList.toArray();
		}
		return new Object[0];
	}
 
	public Object getParent(Object element) {
		if (element instanceof ICnfTreeObject)
			return ((ICnfTreeObject)element).getParent();
		return null;
	}
 
	public boolean hasChildren(Object element) {
		return this.getChildren(element).length > 0;
	}
 
	public Object[] getElements(Object inputElement) {
		return this.getChildren(inputElement);
	}
 
	public void dispose() {
	}
 
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
	}
 
}


DetailLabelProvider.java
public class DetailLabelProvider implements ILabelProvider {
 
	public Image getImage(Object element) {
		return null;
	}
 
	public String getText(Object element) {
		if (element instanceof Detail) {
			return ((Detail) element).getText();
		}
		return new String();
	}
 
	public void addListener(ILabelProviderListener listener) {
	}
 
	public void dispose() {
	}
 
	public boolean isLabelProperty(Object element, String property) {
		return false;
	}
 
	public void removeListener(ILabelProviderListener listener) {
	}
}



Avanced Topic: Override existing Content-Contributions

With the CNF it is possible to replace content that has already been contributed by another plugin, with some content we consider more valuable. cnfdemo.volume – this plug-in uses the possibility provided by the CNF to override existing content-contributions. It removes the sides a and b that are displayed so far due the cnfdemo.details-plug-in and adds the volume instead.



cnfdemo: the former displayed sides a and b of the rectangle are overwritten with the volume


relevant code-snippets

plugin.xml (cnfdemo.volume)



   
      
         
            
            
         
      
   
   
      
         
            
            
         
         
         
      
   




VolumeContentProvider.java
public class VolumeContentProvider implements IPipelinedTreeContentProvider {
 
	@Override
	public Object[] getChildren(Object parentElement) {
		System.out.println("VolumeContentProvider.getChildren() - parentElement.getClass(): " + parentElement.getClass());
		if (parentElement instanceof Rectangle) {
			LinkedList linkedList = new LinkedList();
			linkedList.add(new Volume((ICnfTreeObject) parentElement, "volume: "
					+ new Integer(((Rectangle) parentElement).getA() * ((Rectangle) parentElement).getB()).toString()));
			return linkedList.toArray();
		}
		 return new Object[0];
	}
 
	public Object getParent(Object element) {
		if (element instanceof ICnfTreeObject)
			return ((ICnfTreeObject) element).getParent();
		return null;
	}
 
	public boolean hasChildren(Object element) {
		return this.getChildren(element).length > 0;
	}
 
	public Object[] getElements(Object inputElement) {
		return this.getChildren(inputElement);
	}
 
	@Override
	public void getPipelinedChildren(Object aParent, Set theCurrentChildren) {
 
		// When this method is called theCurrentChildren are instances
		// of Detail.
		// We replace that with instances of Volume.
		//       
		// GUI representation without doing the replacement:
		//   side a: 10
		//   side b: 20
		//        
		// GUI representation after doing the replacement:
		//   volume: 200
 
		theCurrentChildren.clear();
 
		Object[] children = getChildren(aParent);
		for (int i = 0; i < children.length; i++) {
			theCurrentChildren.add(children[i]);
		}
	}
	…
}


VolumeLabelProvider.java
public class VolumeLabelProvider implements ILabelProvider {
	@Override
	public String getText(Object element) {
		if (element instanceof Volume) {
			return ((Volume) element).getText();
		}
		return null;
	}
…
}


Sample code for education (created by other people)

Advanced sample code

Advanced sample code also demonstration how to use filters, contribute actions and more. It is the sample-code that belongs to Michael Elders tutorial series on CNF that could be found here: http://scribbledideas.blogspot.com/2006/07/pdf-versions-now-available.html CVS-Connection for "Paste Connection" in the CVS View: pserver:anonymous@dev.eclipse.org:/cvsroot/eclipse Project: org.eclipse.ui.examples.navigator

Basic sample code – using CNF in RCP

Very basic example, describing how to use CNF within RCP. On the site http://rcpquickstart.com/2007/04/25/common-navigator-tutorial-1-hello-world/ click on the link "Eclipse 3.3" on the bottom of the article.

CNF and the Rich Ajax Platform (RAP)

A short porting-try made the general problems visible that could happen when porting an RCP-Application to an RAP-Application. The CNF plugin has some dependencies on code that is only available in the rcp-version of the ui-plugin (org.eclipse.ui) and not in the rap-verions (org.eclipse.rap.ui). Maybe the following citation also gives a direction for the nearest future of CNF with RAP. Citation (Mon, 23 Jun 2008): http://dev.eclipse.org/newslists/news.eclipse.technology.rap/msg03575.html Hi, sorry to say so, but currently there are no plans for providing the Common Navigator Framework in RAP. Ciao Frank

Some clearance about triggerPoints and possibleChildren

Citation 1 http://scribbledideas.blogspot.com/2006/06/what-does-common-navigator-framework.html In general, if you contribute something, then that thing is a possible child; if you can provide children for something, then that thing is a trigger point. Often, most things that are trigger points are also possible children, but this is not always the case. For instance, many WTP extensions specify trigger points as IProjects with specific facets, but they never contribute these types of things to the viewer; they simple augment them with new types of children. You want to identify all of your model elements as trigger points and possible children; then add to the trigger points the nodes that you begin growing from the tree. In this case, this may be the root node of your model. Citation 2 http://dev.eclipse.org/blogs/francis/2009/01/15/community-comments-needed-for-common-navigator-label-providers/ A secondary problem is that the current documentation is bad: The current documentation for the NCE states that: The triggerPoints expression describes the elements that will cause this extension to be invoked for either children or for labels. The possibleChildren expression describes the elements that the extension may be able to provide a parent for. Clients should describe all elements that could be set as the selection to ensure that the link with editor support can properly expand to the right node. … and … A navigator content extension defines a content provider and label provider that can be used to provide children whenever an element matches the triggerPoints expression and also to provide a parent whenever an element matches the possibleChildren expression. The above statements are not correct with respect to label providers. Label providers use the possibleChildren expression, not triggerPoints expression. The documentation will be corrected to match the current behavior, since changing the behavior to match the documentation could break many things.

Further reading

Documentation in the Eclipse Help http://help.eclipse.org/ganymede/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/cnf.htm Digital Paper Napkin - Sample Code and tutorials from the CNF-Author http://scribbledideas.blogspot.com/ What does the Common Navigator Framework (CNF) help me do? http://scribbledideas.blogspot.com/2006/06/what-does-common-navigator-framework.html Common Navigator Tutorial 1: Hello World http://rcpquickstart.com/2007/04/25/common-navigator-tutorial-1-hello-world/ New in Eclipse 3.5: An Extension-Wizard for a Common Navigator http://swik.net/Eclipse/Eclipse+Tips/Easiest+way+to+create+a+Common+Navigator/cp0zx Newsgroup http://dev.eclipse.org/newslists/news.eclipse.technology.rap/ Common Navigator Framework Use Cases – Discussion for next Eclipse release (3.5) http://wiki.eclipse.org/Common_Navigator_Framework_Use_Cases Displaying Non-Resource Content Using the Common Navigator Framework http://aashishpatil.blogspot.com/2006/07/displaying-non-resource-content-using.html Common Navigator and Other Things – Blog http://dev.eclipse.org/blogs/francis/

Keine Kommentare:

Kommentar veröffentlichen