Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Working XML: The Eclipse task list

And other considerations for Eclipse plug-ins

Benoit Marchal (bmarchal@pineapplesoft.com), Consultant, Pineapplesoft
Photo of Benoit Marchal
Benoît Marchal is a Belgian consultant. He is the author of XML by Example, Second Edition and other XML books. Benoît is available to help you with XML projects. You can contact him at bmarchal@pineapplesoft.com or through his personal site at marchal.com.

Summary:  After more than a year, Benoît returns to the XM (XSLT Make) project. He reports on changes with the Eclipse platform and embarks on a major update that will integrate XM more tightly with Eclipse. As a starting point, he looks into a simple interface enhancement that is often requested: support for the problem and task lists or, more specifically, support for markers. As you will see, working with these lists requires an indirection. He also looks into Eclipse's own resource management and discusses techniques for writing code that works equally well from within Eclipse and from the command line. Share your thoughts on this article with the author and other readers in the accompanying discussion forum. (You can also click Discuss at the top or bottom of the article to access the forum.)

Date:  22 Oct 2004
Level:  Intermediate

Activity:  10715 views
Comments:  

In this new series, I will revisit an old friend of the Working XML column: XSLT Make, or XM. This will serve as an excuse to work on Eclipse plug-ins. I first introduced XM in July 2001 as the first project in the Working XML column. It is a lightweight and affordable tool for publishing documents with XML and XSLT.

In October 2002, I decided to add a graphical user interface to the XM tools. Instead of developing a whole interface from scratch, I turned to a nascent IDE: Eclipse. I chose Eclipse because it was extensible, written in the Java language, and offered a fantastic widget library.

Revisiting XM gives me a chance to improve the Eclipse integration in two areas: I will fix an annoying user interface limitation (in this article) and rewrite the core XM engine to better integrate with Eclipse. In the process, I will also make it more extensible and powerful. Work on the core XM engine is scheduled for the next two articles.

Some history

XM has been around for a while. It has proven its worth in numerous projects, both in my consulting practice and for readers. For example, I use XM as a teaching tool, to manage Web sites for my customers, and to publish large volumes of documentation in HTML and PDF. See Resources for articles on the project.

XM benefits

The rationale behind the initial development of XM was to make XML and XSLT more approachable. I needed a simple-yet-effective solution for maintaining Web sites with small to medium-sized teams. I knew XML and XSLT offered a great basis, but at the time I could not find a suitable tool. Finally, I rolled up my sleeves and made my own. The tools available in 2001 were either too simple (they worked on individual files, not a whole site) or too complex (being aimed at large teams).

XM is powerful (I have used it in projects with thousands of pages), but it also is simple enough for small and medium-sized teams.

Two of XM's most important features are that:

  • It works right out of the box. You don't need to prepare complex scripts or write advanced configuration files. With XM, it suffices to place your documents in one directory, your stylesheets in another, and, voila, you're ready to publish.
  • It produces a static site while offering most of the management benefits of dynamic sites. For instance, you only have to edit one stylesheet to change the layout of the site.

The second point may be more controversial, but it has been my experience that hosting a static site requires less work and is more efficient. Some sites require a combination of static and dynamic pages, but hosting much of the site in static mode helps prevent problems: It uses fewer software packages, which reduces the chances of failure. In addition, the site is more responsive because more sophisticated caching is possible. I encourage you to review the original articles (see Resources) for more information on XM's unique features.

XM perspectives

Things change in a couple of years. Today, a handful of open-source projects could address my needs (see Resources). I have worked with some of those projects and, while I don't claim to have extensive experience with them, I have found that some of them are more powerful than XM, but none is as simple to use.

The Eclipse platform has also seen radical changes. Today, Eclipse is one of the most visible open-source IDEs, with thousands of plug-ins available. More important, the documentation has been upgraded and more samples are available. I remember struggling with the source code and debugger to figure out how to achieve certain effects because the documentation was nonexistent. Those days are gone.

Technically, the Eclipse project has moved from version 2.0 to version 3.0. The new API is expected to lay the foundation for many years to come. Fortunately, versions are mostly compatible (and indeed the XM plug-in, written for Eclipse 2.0, works well on Eclipse 3.0), but some changes are not backwards compatible. It's a good idea to clean up the code and adopt the new APIs where possible.

This new series has two goals:

  • To improve the Eclipse integration. Although functional, the original plug-in has some rough edges. By integrating more closely with Eclipse resource management, I hope to smooth them down a bit.
  • To rewrite the core engine. I have used XM in many projects. In some projects, I have met the limits in my original design of the core engine and have had to implement workarounds. It is time to fold these changes into the project.

Eclipse resource management

As I have said many times in past columns, Eclipse is more than an IDE. It is best described as a platform for building an IDE. Eclipse boils down to a system that manages plug-ins. It offers services such as loading plug-ins, managing relationships and dependencies among plug-ins, managing the interfaces between plug-ins (through extension points), and more.

Obviously some of the plug-ins offer services needed by every application, so they are part of the core. SWT, the widget library, is one of these. Other plug-ins, such as the XM plug-in, are more specific and are installed by the user on an ad hoc basis.

One of the core services is the resource management that's available through the org.eclipse.core.resources plug-in. For Eclipse, everything underneath the workspace is a resource. The primary interface for resources is IResource (pretty obvious). The most commonly used descendants are IFile, IFolder, and IProject, which represent files, folders, and projects, respectively.

While related, IResource and JDK's File objects really are different beasts. A JDK File represents an entry on the file system, while an Eclipse IResource adds several layers of abstraction on top of the file system. First, resources have properties, which represent information on the resource that helps plug-ins process the resource. For example, plug-ins can cache the content of the <?xml-stylesheet?> processing instruction as a property. Caching the data as a property saves having to parse the file whenever the plug-in is run. Properties can be stored in memory (they are lost when the user quits the editor) or persisted to the file system.

Also, when a resource is added, removed, or edited, it becomes desynchronized from the file system. IResource records the state of resources and offers methods for synchronizing with the file system. Most important, Eclipse can notify a plug-in of changes to the resources and the file system. When resources are synchronized with the file system, Eclipse passes the plug-in a delta, the list of changes since the last synchronization. Obviously, this enables intelligent builds where only those resources that were modified are recompiled.


Markers and the task list

From the user's point of view, Eclipse support has two problems: XM has its own logic for rebuilding projects and its own logic for reporting errors. Ultimately, both issues arise because XM ignores Eclipse resource management.

I plan to address the build process in the next two articles in the series. For now, I'll concentrate on error reporting.

Markers

Eclipse offers a task list and a problem list for builders and compilers to report errors, as shown in Figure 1. When the user double-clicks an entry, the editor opens with the offending file. Unfortunately, when I was writing the first version of the plug-in, I could not find documentation on how to add entries to the lists, so I skipped it. Instead, the plug-in has its own console, but it does not support double-clicking.


Figure 1. Task and problem lists
Task and problem lists

As it turns out, it is not difficult to append messages to the standard lists, but it takes an indirection. I originally looked for a task list object, but I could not see how to append an entry to the list. It turns out that you don't -- or at least you don't add them directly to the list. To append an error message, you need to create a marker (interface IMarker) on a resource. To remove a message from the list, you remove the marker from the resource. The lists automatically update to reflect the markers.

The createMarker() method is used to create markers. It takes the marker ID as a parameter. The platform defines a few standard marker IDs:

  • org.eclipse.core.resources.marker -- the root of the marker hierarchy
  • org.eclipse.core.resources.problemmarker -- represents problems and error messages; they appear in the problem list
  • org.eclipse.core.resources.taskmarker -- represents to-do items; they appear in the task list
  • org.eclipse.core.resources.bookmark -- represents a file such as the result of a search
  • org.eclipse.core.resources.textmarker -- represents a position in a file such as the location of an error

It is a good idea to define plug-in-specific markers. The IDs for the new markers are declared in the plugin.xml file (like every other declaration in Eclipse). Listing 1 shows a marker declaration, defining an extension on the marker ID (org.eclipse.core.resources.markers). It also declares that the new marker inherits from problemmarker (to show up in the problem list) and from textmarker (to be able to record a line number). Making the marker permanent means that it will be stored between sessions.


Listing 1. Marker declaration
<extension id="marker"
           name="XM Message"
           point="org.eclipse.core.resources.markers">
   <super type="org.eclipse.core.resources.problemmarker"/>
   <super type="org.eclipse.core.resources.textmarker"/>
   <persistent value="true"/>
</extension>

The user can filter messages according to a number of criteria, such as the type of problem (warning, error), the priority, and the marker ID. Defining a plug-in-specific marker helps the user to apply specific filtering rules to the plug-in messages.

A word of warning: It is possible for Eclipse to filter out messages from a plug-in. If you don't see any error message from XM, make sure that a filter did not remove it. To change the filters, click the filter icon in the task list and in the problem list. Make sure that XM markers are selected.

Once you know this trick, it is not difficult to integrate it into XM. From the very beginning, the user interface has been abstracted through the Messenger interface. Messenger defines the methods that the core needs to report errors and progress information. To support the problem list, you just need to write a new implementation of Messenger that creates the appropriate markers, as shown in Listing 2. Note that the begin() method removes all the markers to clean the problem list before a build.


Listing 2. Messenger for markers
package org.ananas.xm.eclipse;

import java.text.MessageFormat;
import org.eclipse.ui.IWorkbench;
import org.ananas.xm.core.Filename;
import org.ananas.xm.core.Location;
import org.ananas.xm.core.Messenger;
import org.eclipse.ui.IWorkbenchPage;
import org.ananas.xm.core.XMException;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.ui.views.markers.MarkerViewUtil;

public class MessengerTaskList
  implements Messenger, EclipseConstants
{
  private IProject project = null;
  private IWorkbench workbench = null;
  private boolean noMarkerSoFar = true;

  private static class ShowMarkerView
    implements Runnable
  {
    private IWorkbench workbench;
    private IMarker marker;
    public ShowMarkerView(IWorkbench workbench,IMarker marker)
    {
      this.workbench = workbench;
      this.marker = marker;
    }
    public void run()
    {
      IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
      if(window == null)
      {
        IWorkbenchWindow[] windows = workbench.getWorkbenchWindows();
        if(windows != null && windows.length > 0)
           window = windows[0];
        else
           return;
      }
      IWorkbenchPage page = window.getActivePage();
      if(page != null)
        MarkerViewUtil.showMarker(page,marker,true);
    }
  }

  public MessengerTaskList(IWorkbench workbench,IProject project)
  {
    if(null == project || null == workbench)
      throw new NullPointerException("null argument in TaskListMessenger constructor");
    this.project = project;
    this.workbench = workbench;
  }

  protected void addMarker(String msg,Location location,int severity,int priority)
    throws XMException
  {
    IResource resource = null;
    if(null == location || location.equals(Location.UNKNOWN))
      resource = project;
    else
      resource = (IResource)location.getFilename().asPlatformSpecific();
    try
    {
      IMarker marker = resource.createMarker(MARKER_ID);
      if(null != location && Location.UNKNOWN_POSITION != location.getLine())
         marker.setAttribute(IMarker.LINE_NUMBER,location.getLine());
      if(null != msg)
         marker.setAttribute(IMarker.MESSAGE,msg);
      marker.setAttribute(IMarker.SEVERITY,severity);
      marker.setAttribute(IMarker.PRIORITY,priority);
      if(noMarkerSoFar)
         showMarkerView(marker);
      else
         noMarkerSoFar = false;
    }
    catch(CoreException e)
    {
     throw new XMException(e,location);
    }
  }

  public void error(XMException x)
    throws XMException
  {
    addMarker(x.getMessage(),x.getLocation(),IMarker.SEVERITY_ERROR,IMarker.PRIORITY_NORMAL);
  }

  public void fatal(XMException x)
    throws XMException
  {
    addMarker(x.getMessage(),x.getLocation(),IMarker.SEVERITY_ERROR,IMarker.PRIORITY_HIGH);
  }

  public void warning(XMException x)
    throws XMException
  {
    addMarker(x.getMessage(),x.getLocation(),IMarker.SEVERITY_WARNING,IMarker.PRIORITY_LOW);
  }

  public boolean progress(Filename sourceFile,Filename resultFile)
  {
    return true;
  }

  public void info(String msg,Location location)
    throws XMException
  {
    addMarker(msg,location,IMarker.SEVERITY_INFO,IMarker.PRIORITY_NORMAL);
  }

  public void info(String pattern,Object[] arguments,Location location)
    throws XMException
  {
    info(MessageFormat.format(pattern,arguments),location);
  }

  public void begin(String source,String target)
    throws XMException
  {
    try
    {
      project.deleteMarkers(MARKER_ID,true,IResource.DEPTH_INFINITE);
      noMarkerSoFar =  true;
    }
    catch(CoreException e)
    {
      throw new XMException(e);
    }
  }

  public void end()
  {
  }

  protected void showMarkerView(IMarker marker)
  {
    Display display = Display.getCurrent();
    if(display == null)
      display = Display.getDefault();
    ShowMarkerView showMarkerView = new ShowMarkerView(workbench,marker);
    display.syncExec(showMarkerView);
  }
}

More abstraction

XM has always been organized around two components: the core, which is independent of Eclipse and offers a command-line interface, and the Eclipse plug-in itself. To port XM to other user interfaces, implement the interfaces such as Messenger (see the previous section) that abstract the user interface. For some projects, I have defined a servlet user interface over Eclipse.

Although I plan to integrate XM more tightly with Eclipse, I also want to retain the command-line option. Both interfaces offer value. For day-to-day operations, I mostly work with the Eclipse environment, but the command-line version is handy for crontab (a UNIX utility for scheduling job execution). To support both modes, I have abstracted resources and files from the XM core engine.

The original XM worked with the JDK's File object, which is the root of most integration problems because, as you have seen, Eclipse does not use File objects. Instead, it uses its own IResource interface. Furthermore, experience has taught me that relying on File is limiting. Eclipse is not the only package that does not use files -- SAX uses InputSource, and JAXP uses Source.

What do you do when your code needs to interface with several different libraries? You abstract the libraries using the proxy pattern (see Resources). In the proxy pattern, one (or more) object offers a common interface to the underlying libraries. The object can be instantiated to forward a request to any of the libraries. The beauty of this pattern is that the calling code need not worry about the library the proxy is forwarding to.

XM introduces the Filename interface to abstract the notion of a file or resource. The Filename interface has been implemented over Eclipse IResource (for use within Eclipse) and over the JDK File object (for use in the command-line version). Listing 3 is the declaration of Filename. The Eclipse-specific version is available with the source code (see Resources).


Listing 3. An abstraction for files and resources
package org.ananas.xm.core;

import java.io.File;
import org.xml.sax.InputSource;
import org.ananas.xm.core.XMException;

public interface Filename
   extends CoreConstants
{
   public boolean isRoot()
      throws XMException;

   public boolean isFile();

   public boolean isFolder()
      throws XMException;

   public boolean exists()
      throws XMException;

   public String getName()
      throws XMException;

   public String getShortName()
      throws XMException;

   public String getSuffix()
      throws XMException;

   public String getProjectPath()
      throws XMException;

   public Filename getParent()
      throws XMException;

   public Filename[] getChildren()
      throws XMException;

   public void setPersistentMetadata(String key,String value)
      throws XMException;

   public void setPersistentMetadata(String key,String[] values)
      throws XMException;

   public void setTransientMetadata(String key,Object value)
      throws XMException;

   public Object getMetadata(String key)
      throws XMException;

   public String getMetadataAsString(String key)
      throws XMException, ClassCastException;

   public String[] getMetadataAsArray(String key)
      throws XMException, ClassCastException;

   public File asFile()
      throws XMException;

   public InputSource asInputSource()
      throws XMException;

   public Object asPlatformSpecific()
      throws XMException;
   
   public boolean hasSamePath(Filename document)
      throws XMException;

   public boolean isDescendantOf(Filename document)
      throws XMException;

   public boolean remove()
      throws XMException;
}

All the classes in the XM core, such as Messenger, have been rewritten to use Filename.


Conclusion

In the course of two years, Eclipse has become the de facto standard open-source IDE for the Java platform, so it makes sense to strengthen Eclipse support for XM.

One of the benefits of the broader adoption of Eclipse is that more documentation is available, making it easier to write plug-ins. Still, while I value Eclipse, I also believe it pays to abstract the core aspects of your plug-in. For XM, I chose to abstract the user interface and the resource manager. In the next installment, I'll start to focus on the the other major flaw in the XM user interface: the Eclipse build.


Resources

About the author

Photo of Benoit Marchal

Benoît Marchal is a Belgian consultant. He is the author of XML by Example, Second Edition and other XML books. Benoît is available to help you with XML projects. You can contact him at bmarchal@pineapplesoft.com or through his personal site at marchal.com.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML, Open source
ArticleID=23523
ArticleTitle=Working XML: The Eclipse task list
publish-date=10222004
author1-email=bmarchal@pineapplesoft.com
author1-email-cc=dwxed@us.ibm.com

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers