Level: Introductory Balaji Krish (balajik@us.ibm.com), Software Engineer, IBM
01 Feb 2003 Decorators, as the name suggests, are used for adorning or annotating resources with useful information. Decorators can be used by plug-ins to convey information about a resource and other objects displayed in different WebSphere Studio Workbench views. This article, with the help of a simple plug-in example, discusses the steps involved in decorating resources. It includes some best-practice approaches while decorating, and discusses performance issues that might arise with decorators. The author assumes you already have a basic understanding of WebSphere Studio (or Eclipse), and know how to create simple plug-ins.
What are decorators?
Decorators are visual cues that convey useful state information associated with objects or resources displayed in WebSphere Studio views. Many of the standard workbench views, such as Navigator, Package Explorer, and the Outline view, show decorations. With decorators, users can get valuable information about the resources. Figure 1 shows a simple custom decoration.
Figure 1. Simple decorator example

In Figure 1, the lock icon next to number 1 is superimposed on the Java icon image of ImageDecoration.java. By number 3, a prefix and a suffix label are added for TextDecoration.java. Number 2, the file NoDecoration.java, does not have any custom decoration.
In this article, I use an example to step through decorating a resource. Since the performance of the UI is affected by the speed with which decorations are performed, I concentrate on efficient decoration approaches. Decoration can be performed on resources and other objects displayed in WebSphere Studio views. Although I discuss decorating a resource, decorations are not limited to resources; you can apply the same techniques to decorate all the objects displayed in WebSphere Studio views.
Decorators are everywhere
To better understand decorators, it is important to be able to spot some of the basic decorations provided by the platform SDK. Figure 1 might have given the impression that decorators are just the custom images annotating the resources with valuable information. However, some of the basic images, such as the problem marker that's shown to alert users about compilation errors, are a good example of decorations provided by Eclipse technology.
Now that you've seen some of the basic decorations provided by WebSphere, let's revisit the Package Explorer view.
Figure 2. Package Explorer view

Figure 2 above shows how WebSphere Studio views can provide a wealth of information about resources and objects in the workspace, ranging from type of resource (a file, folder, or a project), type of file (Java or a text file), and various Java elements. In Figure 2, a problem marker is superimposed on the compilation unit by number 1. The runtime class, by number 2, indicates the method that failed to compile. From the Package Explorer view, with basic decorations provided by WebSphere Studio, users can get details such as reasons for compilation errors in a Java file.
Types of decorations
The display of a resource in a view has two components -- the label, or name of the resource, and an image denoting the type of resource and possibly other state information. Using the WebSphere extension mechanism, we can change the label and the image of the resource. Hence, we have two different types of decorations:
-
Text Label Decorators - decorations on resource labels
-
Image Decorators - decorations on icon images.
The resource name is the base label provided by WebSphere Studio. This is important, because it is the base value to be decorated and the decorator developer can assume that it will at least be part of the input. Plug-in developers can augment information to the base resource label.
Revisit the Package Explorer view to understand more on text and label decorators.
Figure 3. Image and text decorations

In Figure 3, custom decorations are applied to the base decorations provided by WebSphere. Using the numbers in the pointer next to the file names:
- The file NoDecoration.java does not contain any decoration.
- The file ImageDecoration.java has a lock icon, or image decoration, superimposed on the Java icon image.
- The file PrefixAdded.java has text decorations added to the label.
- The file PrefixAndSuffixAdded.java has text decorations added to the label.
- The file ImageAndTextDecorations.java has both image decoration (a lock icon superimposed on the compilation unit) and text decorations (a prefix and suffix added to the base label).
- The file CustomDecorationsWithProblemMarker.java has a problem marker decoration (basic decoration provided by WebSphere) and custom image and text decorations.
So, how did these decorations get there? Let's start our quest for creating custom decorators.
Defining decorators in plugin.xml
At this point you might want to download the code to run the example.
Source code
To run the example or view the source code for this article, unzip the source code for this article. You'll find two zip files included: unzip sourceCodeOfDecoratorPlugin.zip into your plugins/ subdirectory. To run the source code for the example plug-in using lightweight decorators, unzip sourceCodeOfDecoratorPluginUsingLightweightDecorators.zip into your plugins/ subdirectory. The lightweight decorator mechanism should be available with the next version of WebSphere Studio (based on Eclipse 2.1 or higher).
The example plug-in uses most of the best-practices you should follow while decorating a resource. Some of the classes are:
DemoDecorator - decorates the label and image icon of the resources
DemoImages - maintains the image descriptors of the custom decorator image icons.
DemoStore - stores user decoration preferences.
DemoImageRegistry - accesses the image registry to get the images from registry.
DemoLabelDecoratorPreferencePage - individual label decoration preference page provided by DecoratorDemo plug-in.
DemoFilePropertyPage - custom file property page.
OverlayImageIcon - class to overlay images.
The first step in providing custom decoration is to contribute to the org.eclipse.ui.decorators extension point.
<extension point="org.eclipse.ui.decorators">
<decorator
id="com.ibm.decoratordemo.ui.decorator.demodecorator"
label="Decorator Example"
state="false"
class= "com.ibm.DecoratorDemo.ui.decorators.DemoDecorator"
1-> objectClass="org.eclipse.core.resources.IResource"
adaptable="true">
<description>
This is an example to illustrate the use of decorators
</description>
</decorator>
</extension>
|
In the code above,
- id
- Defines the ID of our decorator. There may be many decorators contributed with the decorator extension point. The decorator ID must be unique.
- label
- Defines the label of the decorator for the Workbench Label Decorations Preferences panel (as shown in Figure 4 below) .
- state
- Defines the default state of the decorator. The decorator can be enabled or disabled by default.
- class
- Class that implements the decorator. The class must implement org.eclipse.jface.viewers.ILabelDecorator and is responsible for decorating the original label's image and text with our own annotations.
-
objectClass
- Indicates the class of resources that need to be decorated. If the user requires decoration only on files, the
objectClass element can be changed to denote that the decoration should be performed only on files. To provide decoration only on files, <1> in the sample above should be changed to objectClass="org.eclipse.core.resources.IFile"
- adaptable
- Indicates whether classes that adapt to the resources should also be decorated. The adaptable flag holds significance when decorations need to be performed on different elements that conform to IResource. More discussion on using the adaptable flag is in Decorating Java files.
- description
- An optional sub-element with a short description of the decorator. This will be shown in the Label Decorations Preferences panel as the description of the custom decorator. The default value for the description is an empty string.
 |
Enabling or disabling custom decorators
Individual decorators can be turned on and off from the Label Decorations Peferences panel, which can be accessed by selecting Window->Preferences->Workbench->Label Decorations, as shown by number 1 in Figure 4 below.
Figure 4. Label Decorations panel

The name of the decorator corresponds to the value of the label attribute of the decorator extension (see previous code sample), and the description corresponds to text contained in the description sub-element. In Figure 4, the Decorator Example decorator is checked, as shown next to number 2, while the CVS decoration is turned off. Although the above-mentioned scenario might be typical when the project is not shared with the CVS repository, enabling and disabling the decorators is extremely useful when the decorations performed by two or more decorators conflict. For example, the CVS plug-in might decorate the base image by superimposing it with a custom image while the Decorator Example plug-in might superimpose a different custom image at the same position, conflicting with the CVS plug-in decoration. If the decoration performed by two different decorators on the same resource conflict, you should appropriately enable or disable different decorators to get the required decoration.
It is very important to design custom decorators that don't conflict with basic decorations provided by different WebSphere views. For example, the Package Explorer view decorates Java files with problem markers, placed at the bottom left hand corner, if there are compilation errors. It is a bad practice to decorate resources with custom decoration exactly at the position of a problem marker, and you should avoid this. If the custom decoration is performed at the bottom left corner, then custom decoration and the problem marker decoration, if any, conflict and users will not be able to view the decorations. The solution to this problem is to provide a custom image decoration at the bottom right corner (or top left corner) that does not conflict with the basic image decoration provided by WebSphere.
Let's take a closer look at the class that provides custom decoration. As mentioned, the name of the class should be the same as the value specified in the class attribute. The class must implement ILabelDecorator.
// Class extends LabelProvider because LabelProvider
// provides methods for getting images and text labels
// from objects
public class DemoDecorator extends LabelProvider implements ILabelDecorator
{
public DemoDecorator()
{
super();
}
// Method to decorate Image
public Image decorateImage(Image image, Object object)
{
// Return null to specify no decoration
return null;
}
// Method to decorate Text
public String decorateText(String label, Object object)
{
// return null to specify no decoration
return null;
}
}
|
The default implementation can be used as a template to get started. The decorateImage() and decorateText() methods are used to decorate the image and text respectively. I'll discuss how to decorate a resource using these two methods later.
Contribute your custom decoration with the decorator extension point in a plugin.xml manifest file, using the example in the previous section. Create a class to implement custom decoration, as shown in the code sample above. Note that the Java class name should be the same as the text value of the "class" attribute of the decorator tag in plugin.xml. Compile and run. You should be able to see your custom decoration appear inside Window->Preferences->Workbench->Label Decorations, as shown in Figure 4.
Individual preferences for label decoration
The Label Decorations Preferences panel gives you only two options, either to use a decorator or not. To give users more control over the decoration contents, and a subset of decorations out of a pool provided by a particular decorator, the plug-ins can provide individual Preferences panels. For example, the CVS plug-in provides a Preferences panel (Window->Preferences->Team->CVS-> Label Decorations) that lets you control the presentation and content of decorators.
Figure 5 below shows how plug-ins can use the individual Label Decorator Preferences panel to give you the ultimate control over the decorations. The CVS plug-in gives you control over the choice of decorations, the richness in decorating different resources, and the types of resources that need to be decorated. You can even control the look and feel of decoration, as shown by numbers 1 and 2 in Figure 5.
Figure 5. CVS individual Label Decoration panel

When providing custom decorations, it's important to consider the performance of the UI with and without decorators. You can effectively use the individual decorator Preferences panel to avoid decorations that are expensive to compute, and thus enhance the performance.
An individual decoration Preferences panel is extremely useful when resources are decorated upon receiving external events or notification. For example, a repository provider plug-in (such as org.eclipse.team.cvs.ui) might be collaborating with an external server to decorate resources with a lock icon whenever resources are checked out by some other user. The repository provider plug-in might be listening to thousands of events, and it would be better if the user has ultimate control on what -- and what not -- to decorate.
Since individual decoration Preferences panels are for giving users control of decorations, they should be contributed with the Preferences panels' extension point. Read the articles listed in Resources to learn more about preference panels and ways to create them.
Example plug-in
Now that you know how to declare a custom label decoration, and contribute individual label decoration Preferences panels, let's dive into the example plug-in.
The example shows how to decorate images and text. To keep it simple, users are provided with a custom file Properties panel. The file Properties panel has a custom panel, Decorator Demo File Properties, that provides a control for users to set the "Busy" property. The Busy property indicates that the resource is busy and hence should not be modified. The panel also provides controls for users to set the prefix and suffix values for the resource.
Figure 6. Example plug-in file Properties panel

A file Properties panel can be opened by right-clicking the file and selecting properties in the context menu. Using this panel as shown in Figure 6 above, you can set the busy nature for the file. When set, the busy nature is indicated by a lock icon superimposed on the base image provided by WebSphere. You can set the prefix and suffix values of resource labels, as shown by numbers 2 and 3 in Figure 6.
Individual label decoration Preferences panels are provided to manage the decorations, as shown in Figure 7 below. They give you control over prefix and suffix text decoration (number 2), and project label decoration (number 3). The project is decorated with a default text label decorator.
Figure 7. Example plug-in Label Decoration Preference panel

Now that you've been introduced to the example plug-in, let's go into details.
Beyond the Basics
Before delving deep into actual methods that provide decorations, it is important to understand the concepts related to decorating a resource.
Resource persistent property
Decorators are used to annotate resources with state information. The state information of a resource used for decoration should be persistent across sessions. In the example plug-in, the prefix, suffix, and the busy nature of the resource should be persisted across sessions. There are two ways to persist resource properties. One is the traditional way of associating resources names with their corresponding properties, and storing them in a property file. The second, preferred, method is to use the Eclipse technology API to store resource persistent properties.
Resources can have properties that hold state information. The properties of a resource are declared, accessed, and maintained by various plug-ins and are not interpreted by the platform. There are two types of properties associated with a resource: persistent and session. Persistent properties, as the name suggests, are persistent across sessions, while session properties are maintained in memory and are lost when the project or workspace is closed. Resource properties are deleted when the resources are deleted.
Depending on the utility of the plug-in, plug-in developers can use persistent or session properties. Persistent properties are stored on disk, so are accessible across platform sessions. The example plug-in requires the busy nature of the resource to be persisted across sessions. The persistent property resource API is used to store and retrieve property values by key. The code sample below shows how to set and get persistent properties of resources using qualified names.
// Create a qualified Name for Busy nature of the resource
QualifiedName q1 = new QualifiedName
("com.ibm.Decorator.DecoratorDemo", "Busy");
// Create a qualified Name for Prefix
QualifiedName q2 = new QualifiedName
("com.ibm.Decorator.DecoratorDemo", "Prefix");
// Set the persistent properties
resource.setPersistentProperty (q1, "true");
resource.setPersistentProperty (q2, "Prefix Value");
// Get the value of persistent property q1... The value
// retrieved is "true"
String busyNature = resource.getPersistentProperty (q1);
|
A qualified name is analogous to a key used for accessing and storing property values. Qualified names are composed of two-part names: a qualifier and a local name. The local name ("Busy" and "Prefix" in the code above) could be used by any decorator. So, it is extremely important to provide a unique URI value for the qualifier part. The simplest way to ensure a unique qualifier value is to use the id of your plug-in as the qualifier name.
Where does WebSphere, on its Eclipse technology base, store resource persistent properties? This would be useful to know if you want to remove persistent properties associated with resources in a project, or copy persistent properties to a new location. WebSphere stores resource persistent properties at the location of your workspace/.metadata/.plugins/ org.eclipse.core.resources/.projects/project name/.properties.
WebSphere provides a mechanism to store the sync information associated with a resource. Plug-in developers might want to consider associating "sync info" with a resource, and decorating a resource using the sync information, before using persistent properties. Sync info maintains all the information in memory and writes to disk only on save or a snapshot. Another advantage of using sync info is that changes to sync information are reported in the subsequent delta, while it is difficult to keep track of the persistent property resource changes. Refer to org.eclipse.core.resources.ISynchronizer to know more about sync info and ways to store it for a resource.
Overlaying images
To provide custom decorations on a resource, in addition to the basic decorations provided by WebSphere, you superimpose custom images on the base image. WebSphere provides utility methods to help with overlaying an image over another.
It is a good idea to design your decorators so they do not overlap or conflict with the existing platform SDK decorators. For example, WebSphere provides the problem marker decorator to alert users of compilation problems. The custom decoration should not superimpose images at the same position as the problem marker. The custom decoration also should not lose the problem marker information. Since different custom decorator providers don't have prior knowledge of one another, there is a good chance that custom decoration from two different plug-in providers will conflict. The Workbench Label Decoration panel and the individual Preferences panel provided by different custom decorators give you control over the choice of different decorations.
WebSphere provides an API for overlaying one image over another. The following code sample explains how to overlay an icon image over another image by:
- Creating a class that extends
CompositeImageDescriptor, which is an abstract base class that should be used by image descriptors that synthesize an image from other images.
- Implementing
drawCompositeImage to superimpose a custom image on the base image.
The base image is drawn (see 1 ) and then the image that needs to be superimposed is drawn at the top left corner of the base image ( see 2 ). The example plug-in code OverlayImageIcon.java implements superimposing the custom images on the base image at different locations.
protected void drawCompositeImage(int width, int height)
{
// To draw a composite image, the base image should be
// drawn first (first layer) and then the overlay image
// (second layer)
// Draw the base image using the base image's image data
1-> drawImage(baseImage_.getImageData(), 0, 0);
// Method to create the overlay image data
// Get the image data from the Image store or by other means
ImageData overlayImageData = demoImages.getLockImageData();
....
// Overlaying the icon in the top left corner i.e. x and y
// coordinates are both zero
int xValue = 0;
int yValue = 0;
2-> drawImage (overlayimageData, xValue, yValue)
}
|
It is always good to create the overlay image icons (that need to be superimposed on the base image) once, and share the same image across different views. In the example code below, an image descriptor for a lock icon is created and the image data is returned when requested by the drawImage() method of OverlayImageIcon (in code above). In this way, custom images are shared among objects across different views. The Best practices section also talks about the image registry and how it can be best used to efficiently decorate resources with custom images.
public class DemoImages
{
private static final ImageDescriptor lockDescriptor =
ImageDescriptor.createFromFile (DemoDecorator.class, "lock.gif");
public ImageData getLockImageData()
{
return lockDescriptor.getImageData();
}
}
|
In the example plug-in, a lock icon is superimposed on the base image if the file has its "busy" property set. Figure 8 below shows how a resource's image icon would look in the Package Explorer view. The lock icon is superimposed on the compilation unit and the runtime class instance of ImageDecoration.java, next to numbers 1 and 2.
Figure 8. Superimposing a lock icon - image decoration

Redecorating
When the Workbench starts, the decorator manager checks for enabled decorators (decorators can be enabled using the Workbench Label Decorations panel), and decorates the resources inside different views using the custom decorations provided by these decorators.
The properties of a resource might change at runtime, which will trigger the need for redecoration. For example, users might change a file's Busy attribute using the file Properties panel, and therefore the image decoration must be changed to reflect the change. To redecorate the resources, we fire a LabelProviderChangedEvent. The fired event notifies the different Workbench views that the label provider for the resource has changed. WebSphere calls the decorateImage() and decorateText() methods for the resources whose label provider has changed. A LabelProviderChangedEvent should only be fired when some aspect of the element used to do the decoration changes. They can also be fired when the labels need to be updated due to a change in decoration presentation (such as a change in a Preferences panel for the decorator). Sending these events will update all affected views.
In the code sample below, a LabelProviderChangedEvent is triggered, shown by number 3, to notify different views that the label provider for the resources has been changed and hence they need to be redecorated. (In this sample, the resources list is stored in resourcesToBeUpdated.) The plug-in developers must provide a Runnable that fires a labelProviderChanged event, shown by number 2.
public void refresh(List resourcesToBeUpdated)
{
// resourcesToBeUpdated is a list of resources whose decorators
// need to be changed. The persistent property of the resources
// has been changed and hence its decorators should change
// Check to see whether the custom decoration is enabled
DemoDecorator demoDecorator = getDemoDecorator();
if (demoDecorator == null)
{
// Decorator is not enabled.. Don't decorate the resources.
return;
}
// Fire a label provider changed event to decorate the
// resources whose image needs to be updated
1-> fireLabelEvent(new LabelProviderChangedEvent (demoDecorator,
resourcesToBeUpdated.toArray()));
}
private void fireLabelEvent(final LabelProviderChangedEvent event)
{
// Decorate using current UI thread
2-> Display.getDefault().asyncExec(new Runnable()
{
public void run()
{
// Fire a LabelProviderChangedEvent to notify eclipse views
// that label provider has been changed for the resources
3-> fireLabelProviderChanged(event);
}
});
}
|
If you choose to change the decoration preference using the individual decoration Preferences panel, all the resources in the workspace need to be redecorated. You could easily do this by changing line <1> above to fireLabelEvent (new LabelProviderChangedEvent (demoDecorator)).
IDecoratorManager interface
IDecoratorManager manages custom decorators contributed by the decorator's extension point. Some of the utility methods provided by IDecoratorManager are:
-
ILabelDecorator getLabelDecorator(String decoratorID)
- Returns the label decorator for the specified decorator if it is enabled.
-
boolean getEnabled(String decoratorID)
- Returns whether the specified custom decorator is enabled or not.
-
void setEnabled (String decoratorID, boolean enabled)
- Enables or disables the custom decorator.
There could be many custom decorators contributed by the decorator's extension point. The ID associated with the custom decorator is unique, and should be used to distinguish between different custom decorators. Views that allow decoration of their elements should use the label decorator returned by the getLabelDecorator() method, by 1 below. The custom decorator objects (instance of the class used for decorating resources) can be found using the decorator ID (1). A custom decorator can be enabled or disabled by default using the state sub-element in the plugin.xml manifest file.
/**
* Get the Custom decorator object. This method should be called to get
* the custom decorator object by all methods that try to decorate resources
* @return Custom Decorator Instance if the custom decorator is enabled
* null if the custom decorator is not enabled
*/
public static DemoDecorator getDemoDecorator()
{
IDecoratorManager decoratorManager =
DecoratorPlugin.getDefault().getWorkbench().getDecoratorManager();
// com.ibm.decoratordemo.ui.decorator.demodecorator is the id of the
// custom decorator
// If the decorator is disabled, a null value is returned
1-> return (DemoDecorator) decoratorManager.getLabelDecorator(
"com.ibm.decoratordemo.ui.decorator.demodecorator");
}
|
The custom decorator class used for decorating resources is a singleton. Decorator developers should not use the decorator object when the decorator is disabled, and should never cache the decorator object. The decorator object is disposed when the decorator is disabled and is recreated when the decorator is re-enabled. The utility method getLabelDecorator() returns a null value if the custom decorator is disabled, or a custom decorator with the given decoratorId does not exist (see 1 above).
Best practices
You should take as little time as possible for decoration, because the performance of the UI will be adversely affected by slow decorator code. To reduce the time involved in decorations, some of the best practice approaches are as follows.
-
Use image descriptors to store descriptors of images rather than storing the images
- The
ImageDescriptor class, as the name suggests, is a lightweight descriptor for an image. It contains all the information required to create an image. Image descriptors do not allocate an actual platform image unless specifically requested using the createImage() method. Using image descriptors is one of the best strategies to use when your code is structured such that it defines all the icons in one place and allocates them when needed.
-
Use Image Registry to share images across different views
-
The
ImageRegistry class is used to keep a list of named images. Clients can add image descriptors or SWT images directly to the list. When an image is requested by name from the registry, the registry will return the image if it has been created, or create one from the descriptor. This allows clients of the registry to share images. See the article
Using Images in the Eclipse UI
for more information about managing images in Eclipse.
Images that are added to or retrieved from the registry must not be disposed by any client. The registry is responsible for disposing of the image, since the images are shared by multiple clients. The registry will dispose of the images when the platform GUI system shuts down. Appropriate use of image descriptors and the image registry is important while performing decorations. Because many views participate in decoration, it is important to share the images using the caching mechanism rather than creating images from scratch.
-
Use a separate thread to perform decoration
- By using a separate thread for decoration (to prevent blocking the UI thread), you can avoid "locking" the GUI while decoration is being performed. Separate threads should be used if it is expensive to compute custom decoration. A separate thread should also be used when the frequency of decoration is high. The example plug-in did not make use of threads to decorate or compute the state of the resource, because the decoration and computing the state of the resource was not a complex operation. If all decorations are done in different threads, users might be running short of system resources and should be careful while decorating individual resources in different threads.
 |
Decorating resources
Let's look at the actual methods that decorate the image or the text of resource labels. As mentioned, the class that is responsible for decoration should implement the ILabelDecorator interface. The interface provides two utility methods to decorate the text and image:
-
Image decorateImage(Image baseImage, Object element)
-
String decorateText(String initialText, Object element)
The decorateImage method is used to decorate the object image with additional state information of the resource. The current image of the object can be gotten using the getImage() method of LabelProvider. The method returns an annotated image or a null image if the object need not be decorated. The decorateText method is used to decorate the object label.
The decorateImage() method, shown below, decorates only a file object and does not decorate a project or a folder (see 2). The IResource object (see 1) is used for determining whether the object under consideration is a project, folder, or file. Using the OverlayImageIcon class (not shown), a lock decorator is superimposed on the base image (see 3).
public Image decorateImage(Image baseImage, Object object)
{
// This method returns an annotated image or null if the
// image need not be decorated. Returning a null image
// decorates resource icon with basic decorations provided
// by WebSphere
IResource objectResource;
1-> objectResource = (IResource) object;
if (objectResource == null)
{
return null;
}
2-> if (objectResource.getType() == IResource.FOLDER
|| objectResource.getType() == IResource.PROJECT)
{
// Projects and Folders are not decorated
return null;
}
// Overlay custom image over base image
Image image;
OverlayImageIcon overlayIcon = new OverlayImageIcon(baseImage, "Lock");
3-> image = overlayIcon.getImage();
return image;
// The image should be disposed when the plugin is
// disabled or on shutdown
}
|
The decorateText() method returns null (no decoration) for a project and folder. It decorates the label of a file with owner information. Returning a null value signifies that the decorator is ignored (see 1 below); it does not clear out any existing decorations.
public String decorateText(String label, Object obj)
{
IResource objectResource;
objectResource = (IResource) object;
if (objectResource.getType() == IResource.FOLDER
|| objectResource.getType() == IResource.PROJECT)
{
// Projects and Folders are not decorated in this example
return null;
}
// Decorate the label of the resource with the admin name
String ownerName = System.getProperties().getProperty("user.name");
1-> return label + " ( " + ownerName + " )";
}
|
When to decorate
Just because you know how to decorate does not mean you know when to decorate. There are no strict rules about when to decorate and when not to. Human intuition is the best way to proceed, although there are some factors you could take into consideration, such as:
- The amount of time to perform the decoration.
- The advantages for the user to know about this particular state information.
- Is it possible to provide the state information in a different way?
- Is the decoration in any way interfering with the basic decorations provided by WebSphere?
- The frequency with which the state information changes.
Let's discuss these factors with an example. Assume you have an example plug-in that tries to emulate CVS behavior, such as trying to provide function for developers to check files in or out of a repository. The plug-in collaborates with an external server to receive notification when resources are checked out by other users. (Checking out a file means someone has extracted the file and is making changes to it.) Let's assume the time spent on computing image decoration is 0.5 seconds. The number of times the plug-in receives notification from the external server would be huge since there are many files in the repository and many users are working concurrently.
In this example, it is advantageous for users to know about files that are changed by others. But, can they afford to lose 0.5 seconds for every decoration? No. So the plug-in developers, rather than decorating the image icon, can present the information to users differently, maybe with the file properties view. Improper use of decorators can lead to poor performance and will ultimately lead to plug-in decorators becoming useless.
Caveat lector (reader beware)
I've discussed best practices to reduce the time to decorate image icons. Every image in Eclipse uses operating system resources. You don't want to create 1,000 copies of the same image. It would be nice to use some of the features in WebSphere to cache decorated images and use them for generating similar images.
Let's assume you want to superimpose a lock icon on three text files. There are two different ways to do this, using methods described earlier, to superimpose a lock icon on the base image. The lock icon should be superimposed on the base image for all three text files. It would be better to decorate the first text file, cache the resultant image in the image registry, and use the cached image to decorate the remaining files. In this way, image caching can be used to avoid superimposing the same custom image over the same base image every time you perform decoration. Image caching would be advantageous when the frequency of decoration is huge, with a lot of time spent on calculating the superimposed image. Image caching should be used if the developers know about images that might appear multiple times.
Decorations are performed when Workbench starts initially. It is called when users open a resource, close a resource, expand the resource tree, and so on.
Image caching, although a good technique to reduce the time involved in decorating resources, is not without problems. Some of the inherent problems associated with decoration using the image caching approach are discussed in the next two sections.
Know all the decoration on a resource
Plug-in developers should know all the decoration on a resource. You should know how to generate a key to be used for retrieving images from the image cache. The key is a string describing all the properties of the resource, such as the type of resource (file, folder, or project), type of file (Java or a text file), and marker information associated with the resource. There could be new decorations provided by other plug-ins, depending on a particular custom resource property. Decorators that do not have knowledge of the custom resource property can't differentiate between a resource that has the custom property set and resources that do not have the custom property set. This could lead to wrong decoration.
Decorating Java files
To understand the problems associated with using image caching to decorate Java files, let's revisit the plugin.xml file.
<extension point="org.eclipse.ui.decorators">
<decorator
id="YourDecorator"
label="Decorator Label"
state="false"
class="YourDecorator.class"
objectClass="org.eclipse.core.resources.IResource"
adaptable="true">
</decorator>
</extension>
|
The attributes that are of interest are objectClass and adaptable. ObjectClass indicates the class of resources that need to be decorated. The adaptable flag indicates whether the classes that adapt to the IResource object should also be decorated.
If a user tries to decorate a Java file in a Navigator view, the decorateImage() and decorateText() methods are called on the IFile object. But if the user tries to decorate the Java file in a Package Explorer view, the decorateImage() and decorateText() methods are called on all the Java elements.
Let's see what happens when a user decorates a Java file and the adaptable attribute is set to true. The decorateImage() method is called on all the Java elements for the resource (JavaProject, PackageFragmentRoot, PackageFragment, CompilationUnit (java file), and runtime class). If the adaptable flag is true, the object parameter passed to the decorateImage() and decorateText() methods is an IResource object for compilationUnit and runtime class, while a null is passed for all the other Java elements.
So what's the problem? Let's assume you cache the image (lock icon superimposed on a Java (Compilation Unit) icon) with the property "Java Lock" to denote that it is a Java file with a lock icon superimposed on the base image. You might have cached the image when the decorateImage method was called on the compilation unit. When the decorateImage() method is called on a class file, you get the same property information using the IResource object (see previous
DecorateImage code sample), and hence we decorate the class file with the cached copy. So instead of getting a lock icon on top of a class icon, the class file image icon is represented by a custom lock decorator on top of a Java icon image. Figure 9 shows this behavior.
Figure 9. Overlaying image without image cache

Figure 10. Overlaying image with image cache

Figure 9 and Figure 10 show that users should be careful while using image caching with non-resource files. When image caching is used, the runtime class is represented by a lock icon on top of the java icon (see 2 in Figure 10) instead of a lock icon on top of the runtime class icon. Image caching can't be used because there was no way to distinguish between the different Java elements using the IResource object and its associated properties. To distinguish between the Java elements, one has to depend on JDT core and write specific adapters for Java elements.
Change plugin.xml, which is provided with the example plug-in, so that DemoDecoratorWithImageCaching is the class that implements decoration rather than DemoDecorator. The DemoDecorator object instance used in the file Properties panel and individual Label Decorations Preferences panel should be replaced with an instance of DemoDecoratorWithImageCaching. You should be able to see decoration like the one shown in Figure 10.
Beware: Because of these problems, image caching, although a good technique to reduce the time involved in decorating resources, should not be used.
What's New in Eclipse 2.1
WebSphere Application Server 5.0 is based on the Eclipse 2.0 API. In Eclipse 2.0, plug-in developers had to programmatically overlay the custom images on top of the base image of the objects displayed in the Eclipse views. Eclipse 2.1 introduces a lightweight decorator that will handle the image management issues associated with decoration. It is also possible to declare a lightweight decorator that simply overlays an icon, when enabled, that requires no implementation from the plug-in. Eclipse 2.1 is expected to be released in March, 2003. Let's look at the configuration markup for decorators in Eclipse 2.1.
<!ELEMENT decorator >
<!ATTLIST decorator
id CDATA #REQUIRED
label CDATA #REQUIRED
1-> class CDATA #OPTIONAL // #REQUIRED if lightweight = false
2-> objectClass CDATA #REQUIRED
//deprecated. Make this part of the enablement
3-> icon CDATA #OPTIONAL // required if there is no class
4-> quadrant ("TOP_LEFT" | "TOP_RIGHT" | "BOTTOM_LEFT" | "BOTTOM_RIGHT")
#OPTIONAL //deprecated. Use location
5-> location ("TOP_LEFT" | "TOP_RIGHT" | "BOTTOM_LEFT" | "BOTTOM_RIGHT"|
"UNDERLAY") #OPTIONAL
6-> lightweight (true | false) #IMPLIED
adaptable (true | false) #IMPLIED
state (true | false) #IMPLIED
>
<!ELEMENT description (#PCDATA)>
7-><!ELEMENT enablement (#PCDATA)>
|
In the code sample above, there are several changes in the configuration markup for decorators in Eclipse 2.1. The class that was a required field in Eclipse 2.0 is an optional field in Eclipse 2.1 (see 1). The class attribute represents a fully qualified name of a class that implements org.eclipse.jface.viewers.ILabelDecorator if lightweight is false, or org.eclipse.jface.viewers.ILightweightLabelDecorator if lightweight is true. The default value is false. If there is no class element it is assumed to be true. The objectClass attribute is deprecated in Eclipse 2.1 and is part of the enablement element (2). The icon attribute is new in Eclipse 2.1, and represents the path to the overlay image to apply if the decorator is lightweight (3). Quadrant attribute represents the quadrant to apply the decorator if the decorator is lightweight (4). Quadrant attribute was introduced in M3 and is deprecated in M4 (replaced by location attribute). Location attribute represents the location to apply the decorator if the decorator is lightweight (5). The default value of location is BOTTOM_RIGHT. The lightweight attribute can be used to signify whether the decorator is lightweight or not (6). The enablement sub-element represents the actionExpression used to determine enabled state (7).
Let's look at the example below to understand lightweight decorators.
<extension point="org.eclipse.ui.decorators">
<decorator
id="com.ibm.DemoLightweightDecorator"
label="DemoLightweightDecorator"
state="false"
1-> class="com.ibm.Demo.LightweightDecorator"
2-> lightweight="true"
<enablement>
3-> <objectClass="org.eclipse.core.resources.IResource"/>
</enablement>
</decorator
</extension>
|
Since the lightweight attribute has a true value (see 2), the class com.ibm.Demo.LightweightDecorator (1) should implement org.eclipse.jface.viewers.ILightweightLabelDecorator. The class com.ibm.Demo.LightweightDecorator should provide the text decoration labels and the image descriptor, and need not be concerned with the resource handling.
Let's look at the ILightweightLabelDecorator interface to see how easily decorations can be performed in Eclipse 2.1.
org.eclipse.jface.viewers.ILightweightLabelDecorator interface:
The ILightweightLabelDecorator is a decorator that decorates using a prefix,
suffix and overlay image rather than doing all of the image and text
management itself like an ILabelDecorator.
1->
void decorate(Object element, IDecoration decoration)
calculates decorations based on element.
|
In the code sample above, it's clear that plug-in developers should implement the decorate() method (see 1) to perform both text and image decorations. This is a big difference from Eclipse 2.0, where plug-in developers had to implement decorateText() for performing text decorations and decorateImage() method for performing image decorations. An added advantage in the Eclipse 2.1 lightweight decorator mechanism is that plug-in developers need not be concerned with resource handling, and need only provide the text and image descriptors. When a plug-in developer tries to redecorate a resource by firing a LabelProviderChanged event, Eclipse calls the decorate () method object. The plug-in developers should appropriately set the overlay image descriptors, prefix label, and suffix label using the IDecoration object instance.
org.eclipse.jface.viewers.IDecoration interface:
Defines the result of decorating an element. This interface is not meant to be
implemented and will be provided to instances of ILightweightLabelDecorator.
1->
void addOverlay (ImageDescriptor overlay)
Adds an overlay to the element's image.
2->
void addPrefix (String prefix)
Adds a prefix to the element's label.
3->
void addSuffix (String suffix)
Adds a suffix to the element's label.
|
For the example plug-in, the lock image descriptor (to signify the busy nature of a resource) can be set using the addOverlay() method (see 1 above). The prefix and suffix labels for an object element can be set using the addPrefix() and addSuffix() methods (2,
3).
If a plug-in requires you to provide only image decoration and no text decorations, then the plug-in developer could use a declarative LightweightDecorator. This means plug-in developers need not provide a class to implement ILightweightLabelDecorator, but instead provide the path for the icon image and location where the icon needs to be placed (TOP_LEFT | BOTTOM_LEFT | TOP_RIGHT | BOTTOM_RIGHT | UNDERLAY). Eclipse LightweightDecorator mechanism takes care of resource handling and image decoration.
As I've shown, the new LightweightDecorator mechanism is quite powerful and makes it easy for developers to decorate resources. The source code for the example plug-in implemented using lightweight decorators (Eclipse M4 stable version) is in the Source Code section.
Summary
Decorators are visual cues that convey useful state information associated with objects or resources displayed in WebSphere views. WebSphere provides ways for you to change the image and label decorators. The performance of the WebSphere (or Eclipse) UI can be affected by the efficiency with which decorations are performed. You can use the best practices in this article to reduce the time involved in decoration. An old saying, "Pictures are worth 1000 words - but only if you know the words" aptly describes the use of decorators.
Acknowledgements
The author would like to thank Jan J. Kratky, Patrick McCarthy, Nick Edgar, and Tod Creasey (all at IBM) for providing constructive comments on the article.
Download | Name | Size | Download method |
|---|
| i-wsdeco.zip | | HTTP |
Resources - Download the source code for this article.
- Read the following technical articles from the Eclipse Web site for more information:
- Find articles, trial downloads, and demos designed for developers who use WebSphere at WebSphere Developer Domain.
About the author  | |  |
Balaji is a software developer at IBM. He is currently involved in the development of Eclipse plug-ins for internal IBM products. Balaji got his Masters in Computer Science at Virginia Tech. You can contact him at balajik@us.ibm.com.
|
Rate this page
|