Let's not mince words. XML solutions that were either complex or too simplistic had me frustrated, so in June 2001 I started working on a simple content-management and publishing solution based on XML. My goal with what I called XM (for XML Make) was to deliver a solution that would be simple to start with, yet powerful enough for real projects. I have been distributing it as open source at www.ananas.org.
Here I will show you how to create a user interface for XM. I chose to use Eclipse, an open-source project, to build an adaptable programming environment. What I learned is that the user interface was the most urgent concern. Yet because I am a software developer, I provided a command-line interface that is powerful but goes against the goal of simplicity.
Building an integrated development environment, or IDE, from scratch is a lot of work. But, fortunately, there are two Java open-source projects to help-netBeans and Eclipse. Both aim to develop a flexible Java IDE and both benefit from large industry support. The most visible difference is that netBeans is Swing-based, while Eclipse builds on the Standard Widget Toolkit (SWT).
In a nutshell, Eclipse is a modern version of Emacs, the popular programmer's editor. Emacs is popular because it has been customized for just about any programming language on earth. However, the Emacs user interface is a relic that never adapted to GUI. Eclipse, on the other hand, aims to be as flexible as Emacs while offering a more modern user interface.
In addition, Eclipse is more than an editor. This highly modular and extensible IDE integrates all the tools that developers need. It offers project wizards, an integrated compiler, a debugger and more. The standard distribution ships with Java tailoring, but other languages such as C/C++ and even Cobol are supported through plug-ins, which guarantee portability. (In fact, Eclipse IDE uses plug-ins-including the Java editor, compiler, project wizards and class browsers-to create most of what you see. I could easily write similar plug-ins for XM to put together an XM IDE.)
Understanding Eclipse
Eclipse is the most ambitious editor application programming interface (API) I have ever used.
I have loosely divided the API into two parts: the platform API and the editor API. The platform API deals with plug-ins, Java libraries that the platform loads in response to user requests (for example, when the user opens a Java class file, the run-time loadsthe Java editor), or to offer services to other plug-ins: for example, the SWT is a plug-in.
Plug-ins implement most of the editor-that is, most of what you see on-screen-which means that any aspect of the platform can be customized through plug-ins and the editor API is a set of plug-ins. For Eclipse, a plug-in consists of a Java library and a manifest to describe it. The manifest is an XML file called plugin.xml.
Upon startup, the platform loads the manifests of all installed plug-ins and compiles a registry. It does not load the plug-ins themselves until they are needed (such as when the user edits a file). Plug-ins that are installed but not used have minimal impact on performance. For example, the core user interface plug-ins are loaded at startup to draw the main windows. If the user loads a Java class, the platform then, and only then, loads the Java editor plug-in.
If one class in the plug-in extends the Plugin abstract class, the run-time calls its startup and shutdown methods when the plug-in is loaded and unloaded. At this point, the platform can initialize the plug-in or free resources. Most plug-ins will not need this class.
An important concept is the extension points. Think of extension points as events that cause a plug-in to be loaded. A plug-in registers the extension points of interest into its manifest. When the event is fired (again, they are not actual events but I have found it helpful to think of them that way), the platform loads the plug-in to handle it. For example, the Java editor plug-in registers extension points when a Java document has been loaded.
There's one aspect in the Eclipse API that has caused me many problems: identifiers. Plug-ins and extension points have identifiers, which are used by the platform to manage the plug-ins and extension points. Identifiers are unique strings. So far, so good. Confusion arises because Eclipse constructs its identifiers with the same rules as Java package names.
For example, the editor defines an extension point, org.eclipse.ui.views. Because Eclipse is written in Java, the extension point is implemented through an interface called org.eclipse.ui.IViewPart. I confess that it took me a while to realize that one was an identifier and the other an interface. To further confuse you, there are several packages starting with org.eclipse.ui.views in the documentation.
To avoid this confusion, remember this: Identifiers appear in the manifest (plugin.xml). As the name implies, the run-time uses them to identify elements. However, Java classes (or interfaces whose names may be similar to the identifier) implement most of the concepts worth identifying.
I learn best by experimenting. So to understand the Eclipse platform better, I wrote a simple test plug-in that proved invaluable in deciphering the Eclipse documentation.
The View plug-in I created defines a view in the Eclipse jargon. A view is a window that displays some information. In this case, the plug-in displays the name of the file selected in the navigator window. Granted, it's totally useless except as a learning exercise, but it does deserve some respect: It summarizes several days of my time spent searching through the documentation.
Like almost everything else in Eclipse, views are declared in a system plug-in. My plug-in registers its view through the org.eclipse.ui.views extension point. Note that, as explained above, org.eclipse.ui.views is an identifier, not a Java class. The corresponding Java interface is org.eclipse.ui.IViewPart.
The ViewPart class represents a default implementation of the IViewPart interface. I could have implemented IViewPart, but ViewPart saves some coding.
The createPartControl() method, which is defined in IViewPart, creates the user interface for the view. The editor calls it when the window appears on-screen. For TestView, the user interface is just a label.
TestView also implements the ISelectionListener interface. This interface, also defined by the Eclipse editor, listens for selection events. The navigator view fires this event when the user selects a different document. When it intercepts the event, the plug-in retrieves the file name and changes its label.
A few additional comments on this class: Label, an SWT widget, implements a noneditable field of text. The editor API also defines ISelectionListener, IWorkbenchPart, ISelection and the other "I" interfaces.
Notice the difference between these interfaces and extension points. Extension points control when a plug-in should be loaded. In contrast, ISelectionListener and the other interfaces come into play after the plug-in has been loaded.
To test this plug-in, download Eclipse 2 or Eclipse 3 (I have tested the plug-in with Eclipse 2 and 3.0 milestone 2), then download the test plug-in or compile the code presented in this article. Place everything in the plugins directory under Eclipse, then complete the following steps:
- Launch Eclipse,
- Select Show View from the Window menu,
- Select Other,
- Expand the ananas.org category (the category we defined in the manifest), then choose Name (our plug-in, as defined in the manifest). The platform fires the extension point, which loads our plug-in and opens a new Name window.
Select entries in the package explorer and the view content will be updated.
The above is a basic demonstration of a very basic plug-in, but if you are curious and you would like a more involving example, then download the complete XM plug-in from www.ananas.org.
Benoit Marchal is a consultant based in Namur, Belgium, who writes for Pineapplesoft. He is the author of XML by Example and specializes in XML and Java solutions. He can be reached at www.marchal.com.