eclipse and plugin.xml object lifecycle management

Eclipse uses plugins for modularity. The plugins often specify content directed towards the presentation layer (the UI). In many extensions, class names are specified to indicate which class should be used for certain jface and SWT classes, for example, specifying a jface content provider such as ITreeContentProvider. Since the eclipse plugin actually creates the class using this class name, you do not always have object lifecycle control over it. Note that a class specified in the plugin could implement the IExecutableExtensionFactory or IExecutableExtension interface however you may need to have access to the input object to properly create the specified class. If you need the input data to fully initialize or create the class through those executable extension interfaces, then the pattern below will be useful to you. You can also specify a factory class in extension points instead of a class name. The factory will be used to create the instance object. If the factory knows a well-published location of where to get and configure the objects it returns, you are now in full control of the lifecycle of objects specified in the eclipse extension. Combining the factory and the executable extension mechanism gives you a paramterized factory which is probably sufficient for nearly all scenarios.

One approach around this is to use the facade design pattern and delegate all calls in the content provider to a delegate object. The delegate object is obtained either through other singletons in the application (or well known object locations such as a toplevel spring container) or derived through the input that is provided during an inputChanged() method call.

The code looks something like (you can make some of this a bit more efficient):


package yourpackage;

import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.navigator.IDescriptionProvider;

/**
* A simple class that delegates to another object with the same interfaces.
* Essentially a facade to allow the actual underlying object to be created and
* managed under a lifecycle that can be controlled by the application versus
* the eclipse plugin environment. Typically, the delegate is derived from the
* input in inputChanged(). To integrate with eclipse, define a
* subclass that provides a small amount of configuration information or that
* just acts as a class tag, then finish the configuration in
* inputChanged.
*
*


* If the delegate is null, the superclass method is called or default values
* indicating no or null content are returned.
*
*


* Further extensions are possible in the spirit of the content service in the
* CommonNavigator framework. For example, getDelegate()
* could look up the delegate based on the class.
*
*/
public class DelegatingContentLabelProvider extends LabelProvider implements
ITreeContentProvider, IDescriptionProvider {

protected Viewer viewer;
protected Object input;
private Object delegate;
private static final Object[] NO_CHILDREN = new Object[0];

protected void setDelegate(Object delegate) {
this.delegate = delegate;
}

protected Object getDelegate(Object element) {
return delegate;
}

protected ILabelProvider getLabelProviderDelegate(Object element) {
return (LabelProvider)getDelegate(element);
}

protected ITreeContentProvider getContentProviderDelegate(Object element) {
return (ITreeContentProvider)getDelegate(element);
}

protected IDescriptionProvider getDescriptionDelegate(Object element) {
return (IDescriptionProvider)getDelegate(element);
}

@Override
public Object[] getChildren(Object parentElement) {
if (getContentProviderDelegate(parentElement) != null)
return getContentProviderDelegate(parentElement).getChildren(parentElement);
return NO_CHILDREN;
}

@Override
public Object getParent(Object element) {
if (getContentProviderDelegate(element) != null)
return getContentProviderDelegate(element).getParent(element);
return null;
}

@Override
public boolean hasChildren(Object element) {
if (getContentProviderDelegate(element) != null)
return getContentProviderDelegate(element).hasChildren(element);
return false;
}

@Override
public Object[] getElements(Object inputElement) {
if (getContentProviderDelegate(inputElement) != null)
return getContentProviderDelegate(inputElement).getElements(inputElement);
return NO_CHILDREN;
}

/**
* Subclasses must call to set the viewer and input fields in this class.
*/
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
this.viewer = viewer;
this.input = newInput;
}

@Override
public String getDescription(Object anElement) {
if (getDescriptionDelegate(anElement) != null)
return getDescriptionDelegate(anElement).getDescription(anElement);
return null;
}

@Override
public Image getImage(Object element) {
if (getLabelProviderDelegate(element) != null)
return getLabelProviderDelegate(element).getImage(element);
return super.getImage(element);
}

@Override
public String getText(Object element) {
if (getLabelProviderDelegate(element) != null)
return getLabelProviderDelegate(element).getText(element);
return super.getText(element);
}

@Override
public void dispose() {
delegate = null;
}

@Override
public boolean isLabelProperty(Object element, String property) {
if (getLabelProviderDelegate(element) != null)
return getLabelProviderDelegate(element).isLabelProperty(element,
property);
return false;
}

}

Comments

Popular posts from this blog

quick note on scala.js, react hooks, monix, auth

zio environment and modules pattern: zio, scala.js, react, query management

user experience, scala.js, cats-effect, IO