Saturday, May 27, 2006

Where do we go from here?

So far, I've posted two articles on how to get started with the Common Navigator Framework (CNF). There's alot more that can be done though, and I'd like feedback from you about what you're most interested in. Eventually, most if not all, of the pieces of the framework will have an article, but the prioritization should be driven by the community. To that end, I ask the wildly open end question, what do you want to learn about next?

There are several large posting topics that come to mind right off:

  • Use cases and motivation for an extensible viewer. The posts so far jumped right into building extensions, but some in the community may not see the reason for having an extensible viewer framework. Is this topic worth an article? Does anyone care about the history or the evolution of the CNF? Or the lessons learned along the way? Or what about what it takes to contribute a component to the Eclipse Platform -- turning a product solution into a Platform-worthy API?

  • Contributing or consuming actions. You can contribute actions or use the API in your own viewers to drive generically extensible menus. You can also structure action extensions that are turned on and off with their content extension, or you can structure menu contributions that have dependencies on other menu contributions (such as contributing to a submenu). The CNF supports object and viewer contributions as well as a CNF-specific programmatic menu builder. You can also contribute to the retargetable actions of the workbench based on the selection in the viewer. Which of these articles would be valuable to you?

  • Filters. I covered some of what you can do with filters in the first posting, was this enough? Do you want to see a programmatic example?

  • Sorting. Sorting in a generic viewer is a nontrivial problem because there does not seem to be an intuitive approach. Therefore, one has to be arbitrarily decided. Even then, some may disagree with the limitations that are imposed. Are any of you interested in providing feedback about what you'd expect? Or do you just want to see an example of contributing sorters to a CNF viewer?

  • Drag and Drop. The CNF supports extensibility for drag transfer data types (specific to the viewer) and extensiblity for drop handling (specific to extensions). You can contribute a DND handler for moving items within a viewer or for moving items from a Common Navigator to another an editor or view in the workbench. Would you like to see how to define your own transfer data type for a viewer? Or what about just adding your own drop handler?

  • Reusing the API outside of the org.eclipse.ui.navigator.CommonNavigator view-part implementation. There are many pieces of the CNF that are designed to be reusable outside of the standard view part implementation. Indeed, Michael Valenta's post is a great example of using the API to construct your own custom extensible viewer class, which doesn't depend on org.eclipse.ui.navigator.CommonNavigator or CommonViewer.
Then there are several smaller topics that might be of interest:
  • Defining retargetable actions. The CNF allows extensions to define their own default Open action (e.g., what happens when the user double clicks a node you've added?), as well as all of the other retargetable actions defined by the workbench (see org.eclipse.ui.actions.IActionFactory) or your own custom retargetable actions.

  • Setting text in the status bar of the workbench. CNF extensions may use different text to describe their items in the status bar of the workbench than they use to label the item in the viewer.

  • Using custom fonts and color for items. Who says that plain old font is good enough? Get fancy with it.

  • Using mementos to restore or persist state between sessions. CNF extensions may remember state from session to session, providing consistency in the user's experience.

  • Binding an extension programatically to a viewer. You've seen how to bind an extension to a viewer using the org.eclipse.ui.navigator.viewer extension point, but did you know you can drive this directly through the API?

  • Turning extensions on and off programatically. Again, the CNF allows you to programmatically customize a viewer using the API.

  • Defining and Using Wizard Shortcut actions. The CNF allows extensions to declare wizard shortcut actions based on existing org.eclipse.ui.xxxWizards extensions. The text and icon is automatically picked up from the wizard definition, which means the action's text and icon updates when the wizard definition changes. Consumers of the framework can also reuse the API in their own viewers, without any CNF dependencies to render their own context menu with New, Import, and Export wizard shortcut actions with very little code.

  • Hiding the default toolbar buttons and tabs in the filters dialog. Some viewers don't care about link with editor or collapse all, and some viewers don't want to expose the ability to turn filters or extensions on and off to the user. Care to know how?

  • Adding a link with editor handler. An oddly placed addition to the CNF is the link with editor extension point. It's not really core to something like the CNF, but it's something that most viewers (particularly navigational viewers) need. For custom editors or models, clients can add their own link with editor handlers; would this be interesting to you?
I'll end this posting with a couple of diagrams I used at EclipseCon 2006 to convey a high level conceptual model of how the extension points are structured. Feel free to post your own questions about these diagrams or your own topic ideas.

In each diagram, the boxes with the component icon (the not-green-C, not-purple-I icon) are the elements in the extension point. The other boxes (with green Cs and purple-Is) show the classes or interfaces (respectively) defined by the element. The allowed cardinality is denoted between each element and its child.

The first diagram shows the org.eclipse.ui.navigator.navigatorContent extension point.


The second diagram shows the org.eclipse.ui.navigator.viewer extension point.



Monday, May 22, 2006

Building a Common Navigator based viewer, Part II: Adding Content


In this post, we're going to walk through adding a simple content extension to the viewer we defined in the last example. To avoid getting bogged down in an overly complex content provider and label provider, we will focus on a file structure with a very simple model, in this case a plain old *.properties file. When finished, our content extension will allow us to expand any *.properties file in the Example viewer and render the data in the file right in the viewer.

You may also download the full example as it stands at the end of this posting.

First, let's take a look at what the content extension will look like in the plugin.xml file. If you have read the last post, you'll know that you can build an extension like the following example using the Extensions tab of the Plug-in Manifest Editor. First by selecting Add... on the Extensions page, choosing org.eclipse.ui.navigator.navigatorContent, then using the right-click menu, and following the New > navigatorContent menu path. Alternatively, you can create this extension in the plugin.xml tab of the same editor. Either approach is fine, but the first approach drives the New menu from the Extension Point Schema, so you will see other options that are available in the raw plugin.xml tab.



The extension declares a content extension with the id "org.eclipse.ui.examples.navigator.propertiesContent" with the display name "Properties File Contents". The name is the string used in the "Available Extensions" tab of the Filters dialog.* We will get into the code for the content and label providers shortly, but for now just note that each are specified; you cannot specify just one or the other.

Finally we set some attributes to tell the framework how we would like our extension rendered in the viewer.

  • activeByDefault determines whether the extension should be active in the default configuration (e.g. new workspace).


  • icon determines what icon should be used when referring to the extension in the User Interface


  • priority is used in a couple of different ways. The most prominent is to determine the relative ordering of items in the viewer (highest priority items towards the top of the viewer down to lowest priority items towards the bottom of the viewer). In general, either "normal" or "high" should be sufficient for most extensions, indicating that they should be mixed in with the resources extension (when priorities match, the label is used to sort items alphabetically) or placed just above resources under projects.


Within each <navigatorContent /> element, we can specify many different types of extensions, but before we grow the example, we must describe to the framework when our extension should be invoked. We need to describe when we can provide children, parents, or labels and icons for nodes in the tree. We do this using Eclipse Core Expressions. For now, I'll refer you to the documentation for org.eclipse.core.expressions for more detail on what is possible with these, or you can use the New > ... menu in the Extensions tab to create these easily.

For content extensions, there are two important expressions: <triggerPoints /> and <possibleChildren />.


The <triggerPoints /> expression indiciates which types of nodes in the tree might be interesting to our extension. When the framework finds a node that matches the <triggerPoints /gt; expression, our extension will be invoked to provide elements or children for that node. Our extension may not be the only one to be given the opportunity to provide children, as the framework will aggregate all contributed children under each node.



The <possibleChildren /> expression indiciates which types of nodes in the tree our extension may be able to provide a label or parent for. For your scenarios that must support link with editor, or setSelection() on the viewer, the <possibleChildren /> expression must be accurate and complete.




Once we have an extension defined, we must bind it to the viewers we want it associated with. Specifying a <viewerContentBinding /> indicates that any extension which matches an included pattern is visible to any viewer with the viewerId specified in the <viewerContentBinding /> element. Recall that we saw these in the last posting.

For the example, we will bind our properties content extension (with id "org.eclipse.ui.examples.navigator.propertiesContent" to our Example View (with id "org.eclipse.ui.examples.navigator.view").




Now that we have setup our extension, let's take a look at the code that will actually do the heavy lifting.

First, we need a model. A properties file has an extremely simple structure, and we will model this using just one type of model object, called PropertiesTreeData, which will have three fields: name (the name of the property), value (the value of the property), and container (the file that contains the properties model. One of these model elements will be populated for each property in the *.properties file.

In our example, the properties model will only be loaded when requested through the content provider. The content provider is used by the framework to determine the children of each element in the tree, or for any given element, determine its parent (or parents as there could be potentially more than one).

Our example content provider implements org.eclipse.jface.viewers.ITreeContentProvider to provide information about the tree structure. The Common Navigator framework also supports implementations of the new org.eclipse.jface.viewers.ITreePathContentProvider, but that is out of scope for this example.

The PropertiesContentProvider also handles some of the other functions required by our extension, such as listening for resource changes and updating the model (and viewer) accordingly. We will not go over these in this posting, but you can see how it works in the full source.

For now, we will focus on the viewer integration methods as defined by ITreeContentProvider.



An ITreeContentProvider must implement getElements(Object input), getChildren(Object parent), hasChildren(Object element) and getParent(Object element).

The getElements() method is queried for elements at the root of the viewer. Many implementations will just forward this call to getChildren(), and that's just what we'll do.



The getChildren() accepts an object (in our case either a *.properties IFile or an instance of our PropertiesTreeData model object because we described these in our <triggerPoints /> expression.

In the following implementation, we check to see if the incoming element is an instance of org.eclipse.core.resources.IFile and if the file extension ends in *.properties. If the incoming element meets these criteria, we check our cache of loaded models or attempt to load the model if not cached. The method updateModel() will create a PropertiesTreeData object for each property, and cache what it finds in the cachedModelMap. Check out the full source to see how updateModel() is implemented.



The hasChildren() method is optimized to return true whenever it is called with an IFile with the *.properties extension. The other alternative is to eagerly load the contents of the file to perform the calculation, but that approach comes with an added performance penality.

If called with a PropertiesTreeData model element, hasChildren() will return false, since none of our model elements may have children (a property has no children, but other models clearly may).



The getParent() method will return the IFile that contains the PropertiesTreeData item or null if called with any other object. The Common Navigator framework will continue querying other extensions until a non-null parent is found or all relevant extensions have been queried. Recall that an extension may be asked to provide a parent for an element if that element matches its <possibleChildren /> expression.



Finally, the label provider of our content extension tells the viewer how to render the icon and label for our model elements (PropertiesTreeData). Since we're only conerned with these elements, we do not need to worry about providing labels or icons for any other elements. Other extensions will render the labels and icons for other elements in the tree.

The PropertiesLabelProvider implements org.eclipse.jface.viewers.ILabelProvider and also org.eclipse.ui.navigator.IDescriptionProvider.

ILabelProvider is the default interface required for proving labels and icons by JFace.

IDescriptionProvider is specific to the Common Navigator framework and is used to provide text for the status bar in the lower left hand corner of the Eclipse window.

The methods required by ILabelProvider are getText() and getImage(). We will render the label for our model elements as the "name= value" string for the property (without the quotes). For the icon, we just use one of the shared icons provided by Platform/UI.



The label provider will be queried for labels, icons, or descriptions for any element contributed directly by the extension or for any element that matches the <possibleChildren /> expression for that extension. If null is returned for the icon or the label, the framework will continuing other applicable extensions based on their <possibleChildren /> expressions and viewer bindings. If your extensions wishes to pass on its opportunity to provide a label or icon, always return null.

And that's it. The final view renders properties files right in our Example View.





*-In general, you should externalize your strings, but we will not worry about that for these examples.

Saturday, May 20, 2006

Building a Common Navigator based viewer, Part I: Defining the Viewer

I've started fielding questions about the Common Navigator on the eclipse.platform newsgroup and the message from the community is resoundingly that there are not enough examples. In order to help the community adopt the Common Navigator framework, I'm going to be fleshing out some examples (in the Eclipse repo at dev.eclipse.org:/home/eclipse, where you can login as "anonymous"; see the wiki for more information. I will be documenting these examples here in this blog. Eventually I will intgrate the documentation that evolves here back into the Platform SDK, but I want a forum where potential consumers can provide feedback easily and directly to the distribution media (e.g. this blog).

Specific documentation on the extension points and API is available in the Eclipse Platform Help (Help > Help Content) under the Platform Plug-in Developer's Guide > Reference > API Reference | Extension Points Reference. The relevant extension points are org.eclipse.ui.navigator.viewer and org.eclipse.ui.navigator.navigatorContent. The relevant API packages are org.eclipse.ui.navigator. If you'd like more details on the specifics mentioned in this post, please follow up with these references.


So where to begin? First we're going to configure our plugin that will contain the viewer to add org.eclipse.ui.navigator to its dependencies. Then we're going to define a view part using the org.eclipse.ui.views extension point and specify the org.eclipse.ui.navigator.CommonNavigator class as our view part implementation, which will provide a cradle for our viewer. Then we will define a org.eclipse.ui.navigator.viewer extension to configure the viewer as a Common Navigator. Finally, we can define org.eclipse.ui.navigator.navigatorContent extensions along with bindings (org.eclipse.ui.navigator.viewer/viewerContentBinding) to associate extensions with our viewer.

You don't have to use org.eclipse.ui.navigator.CommonNavigator in order to take advantage of the framework; you could embed content using the org.eclipse.ui.navigator.INavigatorContentService virtually anywhere from dialogs to editors or define your own viewer and view part class, but for this example, we're going to stick to the basics.


Basic Setup

The Common Navigator Framework is a feature that is new to Eclipse 3.2. Therefore, you must have an update to Eclipse 3.2 environment installed. The topic of this post will not get into setting up Eclipse for self-hosting development. I assume that you have Eclipse 3.2 setup and are either self-hosting against a target of your own choice or the default.

Before we do anything, we need to make sure that we have our plugin configured with the necessary dependencies. Either create a new Plugin (File> New> Project: Plug-in Development> Plug-in Project: Set the name and accept the defaults), or open up the Plug-in Manfiest Editor (select the "plugin.xml" file in your viewer of choice (Project Explorer, Package Explorer, Navigator, etc) and double-click or right-click and select Open With> Plug-in Manifest Editor. On the Dependencies tab, click Add... and begin typing org.eclipse.ui.navigator and select it when it is highlighted or when you see it in the list.

Now select the Extensions tab of the editor.

Defining the view part

When defining a Common Navigator enabled viewer, you must declare the actual view part. Eclipse view parts are defined by the org.eclipse.ui.views extension point. A view part is the container for our viewer; it has a tab handle with a label, a place for a view menu (the little triangle in the top right corner of the view part), and can contain any widget that we need to display to the end user. View parts are not specific to Common Navigators, we just need a cradle to hold our viewer.

You can define the view through the Extensions tab by selecting Add... and begin typing org.eclipse.ui.views until you see it in the list. Then right-click on the entry that is created under the "All Extensions" tree named org.eclipse.ui.views and choose New > view. This will create the element in the plugin.xml file.

When you select the view element, the available attributes for the element should appear as editable textboxes and widgets on the right. We are going to enter the following values (without the quotes):

id: "org.eclipse.ui.examples.navigator.view"
name: "Example View"
class: "org.eclipse.ui.navigator.CommonNavigator"
icon: "icons/filenav_nav.gif" (or your own)
allowMultiple: false

Now select the "plugin.xml" tab of the editor. You should see something like the following. You can choose to create a category as in the diagram, but be sure to indicate which category your viewer should be associated with by specifying the "category" attribute of the viewer. Otherwise, your viewer will appear in the Other category of the Show View dialog (Window > Show View > Other...).



Defining the viewer configuration

Once we define the view part, we need to define a configuration extension for our view part (org.eclipse.ui.navigator.viewer) which will tell the framework some basic things like which view part is a Common Navigator, what the structure of the popup menu should be, and whether to respect object contributions, in addition to properties that influence the default behavior of the viewer.

Open the Extensions tab again on the editor and click Add...; when the dialog appears begin typing org.eclipse.ui.navigator.viewer until you see it in the list and then select it. Right-click on the new org.eclipse.ui.navigator.viewer element in the "All Extensions" tree and choose New > viewer. The element will allow us to associate an abstract Common Navigator viewer with a specific view part. The abstract viewer is what extensions are associated with, regardless of whether the viewer is embedded in a view part or elsewhere. So for this example, we'll use the id we used above in our org.eclipse.ui.views extension ("org.eclipse.ui.examples.navigator.view"). Select the element in the view (which will be labeled name-of-your-plugin.viewer by default) and change the viewerId in the text box that appears on the right to "org.eclipse.ui.examples.navigator.view" without the quotes.


Some comments on popup menus. The Common Navigator framework has two means to allow viewers to customize their menus. The first way to configure the popup menu is to specify the popupMenuId of viewer to accept the default separators and group marker ids for the menu. The default values are documented under the org.eclipse.ui.navigator.viewer extension point, but are reproduced below for convenience. Alternatively, you could choose not to specify a popupMenuId and instead specify a <popupmenu /> element as a child of the <viewer /> element. Here, you may define your own separator and group markers for your downstream clients to add to. If you expect to bind the content or actions of the content extensions provided by the Platform (Resource, Java(tm)), then it is recommended that you use the default groupings.

"group.new" separator="true"
"group.goto"
"group.open" separator="true"
"group.openWith"
"group.edit" separator="true"

"group.show" separator="true"

"group.reorganize"
"group.port"

"group.generate" separator="true"
"group.search" separator="true"
"group.build" separator="true"
"additions" separator="true"
"group.properties" separator="true"

Some comments on options. The < options /> element under the <viewer /> element allows you to specify String-based name=value pairs. There are a few options used by the base framework (documented in the Examples section of org.eclipse.ui.navigator.viewer), but the mechanism can be used by your own viewers or extensions to provide extra configuration to extensions bound to the viewer. See org.eclipse.ui.navigator.INavigatorViewerDescriptor for the API to read the properties specified in a <viewer />element. You can get to the viewer descriptor via the org.eclipse.ui.navigator.INavigatorContentService of a Common Navigator viewer.

Once you have set the viewerId, you can select the "plugin.xml" tab to see the following element defined.



After defining the viewer, we will need to add content extensions and bind them to the viewer. I'll cover this in more detail in the next post. For the example we'll simply bind the default resource content (defined by the org.eclipse.ui.navigator.resources plugin) to our viewer.

Go back to the Extensions tab, right-click on the org.eclipse.ui.navigator.viewer extension and choose New > viewerContentBinding. Select the viewerContentBinding element and set the viewerId to the id we are using for our example viewer ("org.eclipse.ui.navigator.examples.view").
Right-click on the new viewerContentBinding element, and choose New > includes. Finally, right-click on the new includes element
and choose New > contentExtension. For the pattern attribute of the contentExtension we will specify "org.eclipse.ui.navigator.resourceContent" without the quotes, which is the identifier used by the org.eclipse.ui.navigator.resources plugin to identify the Resource extension.

On the "plugin.xml" tab, we should have something like the following:



You may now launch the workbench (Run... > New Eclipse Application) to see the viewer in action. We haven't bound any actions to it yet, so you'll need to use either the Resource Navigator or the Package Explorer to create projects that you can see in the viewer. We'll add Resource actions and filters next, but for now, take notice of the default behavior associated with the viewer. The Collapse All button, the Link with Editor button, the Filters button (along with a Filters ("Available Filters") tab and an Extensions ("Available Content") tab.

To bind the resource filters, we need to add another contentExtension element to our includes. Specify "org.eclipse.ui.navigator.resources.filters.*" as the identifier. This will pick up all resource filters defined by org.eclipse.ui.navigator.resources. Alternatively, you could select the individual filters you want by look at the plugin.xml file of that plugin and picking out the identifiers.



Select Run... again and verify that you now have several resource filters in your "Available Filters" tab of the Filters dialog.

Next we'll bind the default resource actions. To bind actions, we need to add a viewerActionBinding to the root of our extension point. Right-clikck org.eclipse.ui.navigator.viewer and select New > viewerActionBinding. Make sure the viewerId is the correct, and then add an includes element that has a actionExtension element. For the pattern attribute, specify "org.eclipse.ui.navigator.resources.*". Now when you Run... the workbench, you'll see New, Import, Export, and Refresh by default in the empty viewer, and other actions approximating the Resource Navigator when you select elements in the viewer.

Your plugin.xml at this point should look like:




"How does he know all the right ids? This is a little too "magic number for me!" To locate the identifiers of content extensions you would like to bind to your viewer, you can peruse the plugin.xml files of plugins that define the extensions. You can use the Plug-ins view to make this easier (Window > Show View > Other...: PDE > Plug-ins) or you can open the plugin.xml file of org.eclipse.ui.navigator.navigatorContent (using the Plug-ins view, even) and on the Extension Points tab, select navigatorContent. Then click Find References to locate all extensions in your workspace that define content or action extensions you might want to use in your own viewer. Content extensions are defined by the navigatorContent element, so use the id attribute of navigatorContent extensions you would like to bind when you define contentExtension elements under viewerContentBindings for your viewer. Action extensions are defined by the actionProvider element, so use the id attribute of the actionProvider extensions you would lik to bind when you define actionExtension elements under viewerActionBindings for your viewer. Keep in mind that actionProvider that are nested under navigatorContent extensions are automatically bound when their enclosing content extension is bound to a viewer.

Defining Filters

The last thing we are going to do today is define a filter for our viewer using Eclipse Core Expressions. Documentation on the Core Expressions framework is available with the org.eclipse.core.expressions plugin. We are going to use the property testers defined by org.eclipse.core.resources to test for a specific project nature, and hide all projects which do not have this project nature.

So back on the Extensions tab of the Plug-in Manifest Editor, select Add... and choose org.eclipse.ui.navigator.navigatorContent. Then add a new commonFilter element to this extension (same as before: right-click on the org.eclipse.ui.navigator.navigatorContent element, choose New > commonFilter). Change the suggested id to "org.eclipse.ui.examples.navigator.filters.hideNonJavaProjects", and change the name to "Hide non-Java projects". Instead of specifying a class implementation, we are just going to define the expression in terms of XML. Right-click on the commonFilter element and select New > filterExpression. From here, we can continue using the right click menu to create an expression like the following:


You can also set the activeByDefault attribute in the extension to determine whether the filter is active in the default configuration.

Finally, we need to define a contentExtension as part of our viewerContentBinding for this viewer. Be sure to use the id of our new filter, or a generic pattern (like "org.eclipse.ui.examples.navigator.filters.*") to bind this filter and all future filters that we (or our downstream contributions) define that match this id. Your final plugin.xml should look like the following:



Now Run... the workbench one last time. Create a few Simple projects and a few Java Projects. When the "Hide non-Java Projects" filter is enabled, the Simple projects should disappear from the view.


Why would scribbledideas be interesting to read?

I started scribbledideas to better engage in the global discussion that is the internet. I hope to use this forum to post thoughts and information on some of my professional activities, solicit feedback from consumers of technology I have a hand in at my day job, and perhaps throw out an occasional review of a good cigar or perhaps even a good scotch.

For the most part these days, my development efforts focus on Eclipse or Eclipse-based commercial products produced by IBM. In the open source arena, my most recent contribution is the Common Navigator Framework, but before that I had a hand in many of the visceral frameworks in the Eclipse Web Tools Platform (WTP) relating to project structuring, EMF model management, and J2EE tooling. Currently, I am one of several IBM committers to the new Eclipse Services Oriented Architecture (SOA) Tools Platform.

I've been a Java developer for about seven years in various domains from assembly line management software, to enterprise Business to Business communcations, to Eclipse-based desktop software. My interests these days are in the design of solid, robust Application Programming Interfaces (API) and encapsulated, evolveable frameworks.

As an undergrad, I participated in several student research competitions, including taking First Place in the ACM/SIGCSE International Student Research Competition, twice (2001 and 2003); and First (2001) and Third (2003) respectively in the ACM Grand Finals International Student Research competitions. I was fortunate to be able to particpate in these activities as part of the research group run by my undergrad advisor, Hayden S. Porter, of Furman University, where I received a B.S. in Computer Science in 2003. Currently, I am pursuing an M.S. in Computer Science part time from the University of North Carolina at Chapel Hill.

My outside interests (as I alluded to before) center around watching boxing, fine bourbon or scotch, and cigars. I also like grilling meat over fire and cooking. Of course, these activities are always enhanced with good music or if carried out at fine restaurants, jazz clubs or cigar cafes.