/**
* A view's main interface to the world.
*/
public class Site {
/**
* Create a view site, given the plugin that
contains the implementation for the view
*/
public Site(Plugin viewPlugin) {
}
/**
* Return the view's ID
*/
public String getId() {...};
public IActionBars getActionBars() {...};
/**
* Clean up any leftover resources allocated by
the site
*/
public void dispose() {
};
}
/**
* Base class for all views.
*/
public abstract class View {
private Site mySite;
public View(Site viewSite) {
mySite = viewSite;
}
public Site getSite() {
return mySite;
}
public abstract String getTitle();
public void dispose() {};
}
/**
* A factory that can create views by ID.
*/
public ViewFactory {
public static View createView(String
viewExtensionId, Site viewSite) {
//... create a view and return it
}
}
/** * A view's main interface to the world. */public class Site {
Plugin plugin;
public Site(Plugin
viewPlugin) {
plugin = viewPlugin;
}
// ...all other Site methods omitted...
public void logError(Throwable t) {
plugin.getLog().log(new Status(..., t));
};
}
/**
* A view's main interface to the world.
*/
public class Site {
// .... everything in the above version of Site plus
this:
Map allocatedImages = new HashMap();
public Image createImage(ImageDescriptor toCreate) {
Image image =
toCreate.createImage(true);
allocatedImages.put(toCreate, image);
return image;
}
public void destroyImage(ImageDescriptor toDestroy) {
Image image =
allocatedImages.get(toDestroy);
if (image != null) {
allocatedImages.remove(toDestroy);
image.dispose();
}
}
public void dispose() {
Collection values =
allocatedImages.getValueSet();
Iterator iter = values.iterator();
while (iter.hasNext()) {
Image img =
(Image)iter.next();
img.dispose();
}
}
}
/**
* Adapter for a view site that allows allocation/deallocation of
images
*/
public interface IImageAllocator {
public Image createImage(ImageDescriptor toCreate);
public void destroyImage(ImageDescriptor toDestroy);
}
/**
* Implementation of the image allocator adapter provided via XML
* by some plugin.
*/
public class ImageAllocator implements IImageAllocator, IDisposable {
public Image
createImage(ImageDescriptor toCreate) {
...
};
public void destroyImage(ImageDescriptor toDestroy) {
...
};
public void dispose() {
// Dispose any images leaked through
createImage
};
}
/**
* Next attempt at a site interface
*/
public class Site implements IAdaptable {
List disposableAdapters = new ArrayList();
public Object getAdapter(Class adapterType) {
Object result = //...some
magic that looks through the adapter extension point and
//creates an instance of ImageAllocator
if (adapter instanceof IDisposable) {
disposableAdapters.add(adapter);
}
return result;
}
/**
* Allow all the adapters to clean up anything
they allocated.
*/
public void dispose() {
Iterator iter =
disposableAdapters.iterator();
while(iter.hasNext()) {
IDisposable next =
(IDisposable)iter.next();
next.dispose();
}
}
}
/**
* Base class for all views.
*/
public abstract class View {
private IAdaptable mySite;
public View(IAdaptable viewSite) {
mySite = viewSite;
}
public IAdaptable getSite() {
return mySite;
}
public abstract String getTitle();
public void dispose() {};
}/**
* My implementation of a view
*/
public class MyView extends View {
IImageAllocator allocator;
public MyView(IAdaptable viewSite) {
allocator =
(IImageAllocator)viewSite.getAdapter(IImageAllocator.class);
if (allocator == null) {
throw new Exception("I
can't exist without images!");
}
}
//...
}
/** * My implementation of a view */public class MyView extends View { IImageAllocator allocator; public MyView(IImageAllocator imageAllocator) {
allocator = imageAllocator;
} //...}
/** * My implementation of a view */public abstract class Site implements IAdaptable {
public abstract Object getAdapter(Class adapter);
public abstract void dispose();
}/** * "Hello world" view using the component framework */public class HelloWorldView { public HelloWorldView(Composite parent) { Label helloWorld = new
Label(parent, SWT.NONE);
helloWorld.setText("Hello
world"); }}
The HelloWorldView component depends on one other object: a
Composite
created for it by whoever instantiated the view. Compare this with the
same view written using the existing API:/** * "hello world" view using the Eclipse 3.0 API. */public class HelloWorldView extends ViewPart { public void createPartControl(Composite
parent) { Label
helloWorld = new Label(parent, SWT.NONE);
helloWorld.setText("Hello
world"); } public void setFocus() { }}<extension
point="org.eclipse.ui.views"> <view name="Title Test View" icon="icons\view.gif"
class="org.eclipse.ui.mytest.HelloWorldView" id="org.eclipse.ui.mytest.HelloWorldViewID"> </view></extension>/** * Creates a view of the given type inside the given
composite.
The caller must dispose the container once they are done with it. * * @param viewId id of the view extension to use * @param parentComposite parent composite for the view * @return an IContainer that contains all components needed
for the view */IContainer createView(String viewId, Composite parentComposite);/** * Main interface to a component. */public interface IContainer extends IAdaptable { public void dispose();void
createHelloWorldViewInADialog(IWorkbenchPage page) { Display display = Display.getDefault() Shell shell = new Shell(Display.getDefault()); shell.setLayout(new FillLayout()); // Create the view and its widgets IContainer myView =
workbenchPage.createView("org.eclipse.ui.mytest.HelloWorldViewID",
shell); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch ())
display.sleep (); } // Dispose the view myView.dispose();}/**
* Factory for IContainer instances. The default factory is
returned by
* Components.getFactory(). Clients wishing to implement their own
specialized
* factories should call createDerivedFactory() to get access to a
factory
* whose behavior can be modified programmatically. Not intended
to be
* implemented by clients.
*
* @since 3.1
*/
public interface IContainerFactory {
/**
* Creates and returns a new IContainer
instance, given the
* implementation class for its component. The
caller MUST call IContainer.dispose()
* when it is done with the component. The
factory does not need any prior
* knowledge of the component class.
*
* @param componentImplementation concrete
class to be instantiated by the factory. The class
*
must be a valid component (it must have exactly one constructor which
only
*
references other component interfaces known to this factory).
* @return a newly constructed
<code>IContainer</code> instance
* @throws CoreException if unable to create
the component
*/
public IContainer createContainer(Class
componentImplementation) throws CoreException;
/**
* Creates a specialization of this factory. By
default, the specialized
* factory will have the same behavior as its
parent. However, the derived
* factory can add, or override the
implementation for any components.
*
* @return new factory instance that allows
individual components to be overridden.
* By default, the derived factory will
delegate all of its behavior to the receiver.
* Changes in the receiver will affect the
derived factory.
*/
public IMutableContainerFactory
createDerivedFactory();
}
// Get the global factoryIContainerFactory viewFactory =
Components.getFactory();// Create the Composite for the viewComposite viewComposite = new
Composite(parentComposite, SWT.NONE);viewComposite.setLayout(new FillLayout();// Create a specialized factory that knows about the view's
composite and plugin bundleIMutableContainerFactory derivedFactory =
viewFactory.createDerivedFactory();// Add the view's composite to the factory so that it can be seen
by the view constructor
derivedFactory.addComponentInstance(viewComposite);// Add the view's plugin bundle to the factory (not
required in this example, but this// is recommended practise for any component created from an
extension point).derivedFactory.addComponentInstance(pluginBundle);// Create the view. Provide its constructor and some
context (the page that created it).IContainer view =
derivedFactory.createContainer(HelloWorldView.class);// Do something with the view// ...// Now clean upview.dispose();viewComposite.dispose();
/** * ComponentAdapter for creating and managing SWT Composites. */public class CompositeFactory extends ComponentAdapter { private Composite parent; /** * The SWT composites created by this
factory will all be children of the given * composite. */ public CompositeFactory(Composite parent) { this.parent = parent; } protected Object create(IAdaptable
availableAdapters) throws CoreException { // Create a new Composite Composite newChild =
new Composite(parent, SWT.NONE); newChild.setLayout(new
FillLayout()); return newChild; } protected void dispose(Object toDispose) {
((Composite)toDispose).dispose(); } public String getInterfaceName() { // Return the fully
qualified class name of the Composite class. The framework will call // this method to determine
what type of interface will be implemented by the objects
// constructed by this factory. return
Composite.class.getName(); }}// Get the global factoryIContainerFactory viewFactory =
Components.getFactory();// Create a specialized factory that knows about the view's
composite and plugin bundleIMutableContainerFactory derivedFactory =
viewFactory.createDerivedFactory();// Add a factory that can create the view's compositederivedFactory.addComponentFactory(new
CompositeFactory(parentComposite));// Add the view's plugin bundle to the factory (not
required in this example, but this// is recommended practise for any component created from an
extension point).derivedFactory.addComponentInstance(pluginBundle);// Create the view. Provide its constructor and some
context (the page that created it).IContainer view =
derivedFactory.createComponent(HelloWorldView.class);// Do something with the view// ...// Now clean upview.dispose();
/** * Component interface: * * Provides a context for reporting and logging exceptions. */public interface IErrorContext { /** * Create an IStatus error describing the
given
throwable */ public IStatus createStatus(Throwable t); /** * Create an IStatus message with the
given
severity, message, and (optional) throwable */ public IStatus createStatus(int severity,
String
message, Throwable t); /** * Logs an IStatus message */ public void log(IStatus status); /** * Logs an exception to the system log */ public void log(Throwable t);}
Here is the associated default implementation:
/** * Default implementation of the IErrorContext interface: * * Creates and logs errors in the context of a plugin bundle */public class ErrorContext implements IErrorContext { private Bundle pluginBundle; public ErrorContext(Bundle
pluginBundle) { this.pluginBundle =
pluginBundle; } public IStatus createStatus(Throwable t) { String message = t.getMessage(); if (message == null) { message =
t.getString(); } return createStatus(Status.ERROR,
message, t); } public IStatus createStatus(int severity,
String
message, Throwable t) { return new Status(severity,
pluginBundle.getSymbolicName(), Status.OK, message, t); } public void log(Throwable t) { log(createStatus(t)); } public void log(IStatus status) { Plugin plugin =
Platform.getPlugin(pluginBundle.getSymbolicName()); plugin.getLog().log(status); }}<extension
point="org.eclipse.core.component.interface"> <interface
class="org.eclipse.ui.workbench.ErrorContext" interface="org.eclipse.ui.workbench.IErrorContext"
childadapter="false"
scope="plugin"> </interface></extension>public class MyView { private IErrorContext errorContext; public MyView(Composite parent, IErrorContext
errorContext) { this.errorContext = errorContext; Button errorButton =
new
Button(parent, SWT.PUSH); errorButton.setText("Log an
exception");
errorButton.addSelectionListener(new
SelectionAdapter() { public void
widgetSelected(SelectionEvent e) {
try {
// Throw a NPE
String myString = null;
String bogusCode = myString.substring(10, 30);
} catch (Exception e) {
// Log the NPE using the error context
errorContext.log(e);
} } }); }}
This view creates a button which, when pressed, will throw a NPE
and log it. The status message will be constructed and logged using the
IErrorContext interface we defined above. The default implementation of
the interface is associated with a
unique component instance, so if we were to create two instances of
MyView we would also end up with two instances of ErrorContext. /** * Provides access to a plugin's preference store in a
manner that
prevents listener leaks. */public interface IPreferences { public String getString(String prefId); public void setString(String prefId, String
value); public void
addListener(IPropertyChangeListener l); public void
removeListener(IPropertyChangeListener
l);}/** * Concrete implementation of the IPreferences interface */public LocalPreferenceStore implements IPreferences, IDisposable { ListenerList listeners = new ListenerList(); Preferences prefs; private class PropertyChangeListener
implements
Preferences.IPropertyChangeListener { /* * @see
org.eclipse.core.runtime.Preferences.IPropertyChangeListener#propertyChange(org.eclipse.core.runtime.Preferences.PropertyChangeEvent) */ public void
propertyChange(Preferences.PropertyChangeEvent event) {
firePropertyChangeEvent(event.getProperty(), event.getOldValue(),
event.getNewValue()); } } PropertyChangeListener listener = new
PropertyChangeListener(); /** * Create a wrapper around the given
bundle's
preference store. */ public LocalPreferenceStore(Bundle
pluginBundle) { Plugin plugin =
Platform.getPlugin(pluginBundle.getSymbolicName()); prefs =
plugin.getPluginPreferences(); prefs.addListener(listener); } private void firePropertyChange(String name,
Object
oldValue, Object newValue) { PropertyChangeEvent event =
new
PropertyChangeEvent(this, name, oldValue, newValue); Object[] listeners =
this.listeners.getListeners(); for (int i= 0; i <
listeners.length; i++)
((IPropertyChangeListener) listeners[i]).propertyChange(event); } /** * This method will automatically be
called
when the component that uses this adapter is disposed. * It should clean up anything that has
been
allocated by the service. */ public void dispose() { prefs.removeListener(listener); } public String getString(String prefId) { return prefs.getString(prefId); } public void setString(String prefId, String
value) { prefs.setValue(prefId, value); }}public LifecycleService
implements IDisposable { public LifecycleService() {
System.out.println("LifecycleService
created"); } public void dispose() {
System.out.println("LifecycleService
destroyed"); }}public LifecycleView implements IDisposable { public LifecycleView(LifecycleService service)
{ System.out.println("LifecycleView
created"); } public void dispose() { System.out.println("LifecycleView
destroyed"); }}org.eclipse.core.component.interface
extension point since the default implementation will guarantee that
the interface always exists even if the parent context doesn't know
about it. However, this situation can occur if a parent and child are
communicating with an interface that wasn't originally intended for use
as a component interface, or if the child requires a variable set of
interfaces.public interface INameService {
public String getName();
}
/**
* A component with no dependencies, but which can optionally
accept an IMemento for initialization.
*/
public class MyComponent {
String myName = "default name";
public MyComponent(IAdaptable optionalInterfaces) {
// Check if an
INameService
service exists. INameService nameService =
(INameService)optionalInterfaces.getAdapter(INameService.class);
if (memento != null) {
myName =
nameService.getName();
}
System.out.println("created component
with name = " + myName);
}
}
// Create a memento with the name
"custom name"
INameService nameService = new INameService() {
public String getName() {
return "custom name";
}
}
// Create a factory which knows about our memento
IMutableContainerFactory context =
Components.getFactory().createDerivedFactory();
context.addComponentInstance(nameService);
// Use the factory to instantiate MyComponent
IContainer component = context.createContainer(MyComponent.class);
// Will print the message "created component with name = custom name"
component.dispose();IContainer component =
Components.getFactory().createContainer(MyComponent.class);
// Will print the message "create component with name = default name"
component.dispose();<extension
point="org.eclipse.core.component.interface"> <interface
class="org.eclipse.ui.DefaultNameService" interface="org.eclipse.ui.INameService"> </interface></extension>
/**
* Default implementation of INameService that will be used if the
parent doesn't explicitly provide one
*/
class NameService implements INameService {
public String getName() {
return "default name";
}
}
/**
* A component with no dependencies, but which can optionally
accept an INameService for initialization.
*/
public class MyComponent {
String myName;
public MyComponent(INameService nameService) {
myName = nameService.getName();
System.out.println("created component
with name = " + myName);
}
}
| Scope path |
Required context |
Description |
| / |
None |
Any service that omits the scope attribute automatically
belongs to the global scope. Services in the global scope can be used
by any other component, but may only depend on other global services.
Global services are not given any context from the application. |
| /plugin |
Bundle |
Components in the /plugin scope
are created in the context of a plugin bundle. All executable
extensions (any component created using an extension point) belong to
the plugin scope. Components in this scope can reference their own
plugin Bundle in their constructor. Services can reference the Bundle
associated with the component that requested them. |
| /plugin/part |
Bundle, Composite |
Components in the /plugin/part
scope are associated with an SWT Composite. Components in this scope
are given their
own SWT Composite. They may change the layout on the given Composite,
but may not change its layout data. The composite will be managed as a
service, so the component does not need to dispose it. Components in
this scope may be used as editors, views, or both. |
| /plugin/part/view |
Bundle, Composite, IWorkbenchPage |
All views belong to this scope.
They are given the IConfigurationElement containing their extension
markup, and an IWorkbenchPage. |
| /plugin/part/editor |
Bundle, Composite, IWorkbenchPage, IEditorInput |
All editors belong to this
scope. They are given the IConfigurationElement containing their
extension markup, an IWorkbenchPage, and the IEditorInput containing
their input. |
| Scope |
Interface |
Description |
| /plugin |
IErrorContext |
Provides facilities for logging
exceptions |
| /plugin |
ISwtResources |
Provides facilities for
allocating SWT resources such as Fonts, Images, and Colors |
| / |
INameable |
Parts can use this service to
change their name, content description, title image, and tooltip. The
default implementation ignores all method calls. |
| / |
IMemento |
Parts will receive a memento
which they can use to load previously-saved state |
| /plugin |
IPartFactory |
Interface that can be used to
create child views, editors, and other UI parts |
| /plugin/part |
IActionBars2 |
Interface used to add to the
toolbar, cool bar, etc. Currently exposed on the view site. |
| Scope |
Interface |
Description |
| /plugin/part |
IFocusable |
Parts can implement this service
to allow their parent |
| / |
IMultiPart |
Parts should implement this if
they contain other parts and have the notion of an "active" child.
Other services can use this service if their default implementation
should redirect to the active child. |
| / |
IPersistable |
Parts may implement this
interface if they wish to save their state between sessions. |
/**
* Parts can implement this interface if they wish to overload the
default
* setFocus behavior.
*/
public interface IFocusable {
/**
* Gives focus to the part
*/
public void setFocus();
}
/**
* Default implementation of the IFocusable service. If a part
doesn't explicitly
* provide an adapter for IFocusable, this implementation will be
used.
*/
public class DefaultFocusable implements IFocusable {
private Composite control;
private IMultiPart activePart;
/**
* Creates the default implementation of
IFocusable, given the main control
* of the part and an IMultiPart that can be
queried for the active child.
*
* @param toGiveFocus main control of the pane
* @param activePartProvider multiplexer that
returns the active part
*/
public DefaultFocusable(Composite toGiveFocus,
IMultiPart activePartProvider) {
control = toGiveFocus;
activePart = activePartProvider;
}
/**
* First, try to give focus to the active
child. If there is no active child, give focus to
* the main control.
*/
public void setFocus() {
// If this part has the notion of
an active child, give that child focus
Object current =
activePart.getCurrent();
if (current != null) {
IFocusable
focusable = (IFocusable)Components.getAdapter(current,
IFocusable.class);
if (focusable
!= null) {
focusable.setFocus();
return;
}
}
// If the part has no children,
then give focus to the part itself.
control.setFocus();
}
}<service
interface="org.eclipse.ui.workbench.services.IFocusable"
class="org.eclipse.ui.internal.part.serviceimplementation.DefaultFocusable
childadapter="true"
scope="plugin/part"/>
/**
* Part that explicitly implements IFocusable.
*/
public class Page implements IFocusable {
Text textField;
public Page(Composite control) {
textField = new Text(control, SWT.NONE);
}
public void setFocus() {
textField.setFocus();
}
}
/**
* Non-multiplexing part that relies on the DefaultFocusable
service
* to give focus to its composite.
*/
public class Page2 {
Text textField;
public Page(Composite control) {
textField = new Text(control, SWT.NONE);
}
}
/**
* Multiplexing part that contains a Page, a Page2, and a checkbox.
* When the checkbox is selected, the first page will be active.
When the
* checkbox is deselected, the second page will be active. Calling
setFocus
* on the MultiplexingView will always give focus to the active
page.
*/
public class MultiplexingView implements IDisposable, IAdaptable {
private IContainer page1;
private IContainer page2;
// Helper class that implements the IMultiPart
interface
private Multiplexer multiplexer = new Multiplexer();
private Button checkBox;
public MultiplexingView(Composite myControl) {
myControl.setLayout(new RowLayout());
checkBox = new Button(myControl,
SWT.CHECK);
checkBox.addSelectionListener(new
SelectionAdapter() {
public void
widgetSelected(SelectionEvent e) {
updateActivePart();
}
});
checkBox.setText("Activate part 1");
// Create the child controls
IMutableComponentFactory childFactory =
Components.getFactory().createDerivedFactory();
// Use the CompositeAdapter class we
created earlier in order to provide each page
// with its own control.
childFactory.addComponentInstance(new
CompositeAdapter(myControl));
page1 =
childFactory.createContainer(Page.class);
page2 =
childFactory.createContainer(Page2.class);
// Activate the initial part
updateActivePart(); }
private final void updateActivePart() {
if
(checkBox.getSelection()) {
multiplexer.setCurrent(page1);
} else {
multiplexer.setCurrent(page2);
} }
/**
* In order to indicate that this is a
multiplexing part, we need to implement
* the IMultiplexer adapter. We simply redirect
to the multiplexer object.
*/
public Object getAdapter(Class adapterType) {
if (adapterType == IMultiplexer.class) {
return multiplexer;
}
}
/**
* Release any resources allocated in the
constructor
*/
public void dispose() {
page1.dispose();
page2.dispose();
}
}// Stupid example
that creates a MultiplexingView, gives it focus, then destroys it
// Create a MultiplexingViewIMutableComponentFactory childFactory =
Components.getFactory().createDerivedFactory();
childFactory.addComponentInstance(new
CompositeAdapter(myControl));
IContainer viewContainer =
childFactory.createContainer(MultiplexingView.class);
// Give focus to the active part
IFocusable focusable = viewContainer.getAdapter(IFocusable.class);
focusable.setFocus();
// Destroy the view
viewContainer.dispose();
This example also shows how the same default implementation can
work for both
multiplexing and non-multiplexing parts. The Page2 class doesn't
implement IFocusable or IMultiPart, but we are still able to
ask for an IFocusable interface and use it to give focus to the part.public interface INameable {
public void setName(String newName);
public void setContentDescription(String
contentDescription);
public void setImage(Image theImage);
public void setTooltip(String toolTip);
}
public class MyView {
public MyView(Composite parent, INameable name) {
name.setName("Some tab text");
}
}
public interface IPartFactory {
public IContainer createView(String viewId,
Composite parentComposite, IWorkbenchPage page, IContainerFactory
services);
public IContainer createEditor(String editorId,
Composite parentComposite, IWorkbenchPage page, IEditorInput
input, IContainerFactory services);
public IContainer createPart(String partId,
Composite parentComposite, IContainerFactory services);
}