| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> | |
| <html> | |
| <head> | |
| <title>Adapters</title> | |
| <meta http-equiv="Content-Type" | |
| content="text/html; charset=windows-1252"> | |
| <link href="../article.css" type="text/css" rel="stylesheet"> | |
| </head> | |
| <body> | |
| <h1>Adapters</h1> | |
| <div class="summary"> | |
| <h2>Summary</h2> | |
| <p>The adapter pattern is used extensively in Eclipse. The use of | |
| this pattern allows plug-ins to be loosely coupled, yet still be tightly | |
| integrated in the extremely dynamic Eclipse runtime environment. In this | |
| article, we show you how to use the adapter framework to make your own | |
| objects adaptable, and adapt other objects.</p> | |
| <div class="author">By Wayne Beaton, The Eclipse Foundation</div> | |
| <div class="copyright">Copyright © 2008 The Eclipse | |
| Foundation.</div> | |
| <div class="date">June 24, 2008</div> | |
| </div> | |
| <div class="content"> | |
| <h2>Introduction</h2> | |
| <p>The adapter pattern is used extensively in Eclipse. The use of | |
| this pattern allows plug-ins to be loosely coupled, yet still be tightly | |
| integrated in the extremely dynamic Eclipse runtime environment. The | |
| adapter framework is used extensively by a wide variety of plug-ins, | |
| including those from the Eclipse platform, other Eclipse projects, and | |
| by the broader community and eco-system. The adapter framework should be | |
| considered as one of the essential parts of the Eclipse platform that | |
| everyone writing Eclipse plug-ins must know about.</p> | |
| <h2>Using the Properties View</h2> | |
| <p><em>This article is not intended to provide thorough | |
| coverage of the Properties view. Rather, the Properties view is used as a | |
| means for demonstrating how adapters can be put to work.</em></p> | |
| <p>The Properties view displays properties for whatever is selected | |
| in the workbench. Selections can occur in many places: the Navigator and | |
| Package Explorer views are two very obvious sources of selection. But | |
| selections can come from other places, even views created by you and | |
| your fellow developers. It's easy to see the Properties view in action. | |
| Just open it and select something in the Package Explorer; the | |
| Properties view will update to reflect the properties for the selected | |
| thing. Here, we see the Properties view (bottom) showing properties for | |
| file selected in the Package Explorer:</p> | |
| <div class="figure"><img src="images/properties.png" /></div> | |
| <p>One of the really interesting things about the Properties view is | |
| that it can display properties for objects that it knows nothing about. | |
| In fact, the Properties view doesn't really know anything in particular | |
| about any of the objects it displays.</p> | |
| <p>You can use the Properties view to show information about the | |
| objects selected in a view that you've created. The first step is to | |
| make sure that the workbench selection service knows about the object | |
| selected in your view. If you're using a JFace <code>TableViewer</code> | |
| or <code>TreeViewer</code>, then this step is easy (if you're doing | |
| something else, <a | |
| href="http://www.eclipse.org/articles/Article-WorkbenchSelections/article.html">"Eclipse | |
| Workbench: Using the Selection Service"</a> by Marc R. Hoffmann will | |
| help you determine how to best contribute the selection). You need to do | |
| is invoke the <code>setSelectionProvider</code> method in your view's <code>createPartControl</code> | |
| method (the important part is marked in bold):</p> | |
| <pre>... | |
| public void createPartControl(Composite parent) { | |
| viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); | |
| viewer.setContentProvider(new ViewContentProvider()); | |
| viewer.setLabelProvider(new ViewLabelProvider()); | |
| <strong>getSite().setSelectionProvider(viewer);</strong> | |
| viewer.setInput(getViewSite()); | |
| } | |
| ...</pre> | |
| <p>Once you have your view contributing to the workbench selection, | |
| you need to make sure that the objects that your view is selecting | |
| contribute properties. The easiest (but not necessary most correct) way | |
| to do this is to have your class implement the <code>IPropertySource</code> | |
| interface:</p> | |
| <pre>... | |
| public class Person <strong>implements IPropertySource</strong> { | |
| private String name; | |
| private Object street; | |
| private Object city; | |
| public Person(String name) { | |
| this.name = name; | |
| this.street = ""; | |
| this.city = ""; | |
| } | |
| public Object getEditableValue() { | |
| return this; | |
| } | |
| public IPropertyDescriptor[] getPropertyDescriptors() { | |
| return new IPropertyDescriptor[] { | |
| <strong>new TextPropertyDescriptor("name", "Name")</strong>, | |
| new TextPropertyDescriptor("street", "Street"), | |
| new TextPropertyDescriptor("city", "City") | |
| }; | |
| } | |
| public Object getPropertyValue(Object id) { | |
| if ("name".equals(id)) return name; | |
| else if ("street".equals(id)) return street; | |
| else if ("city".equals(id)) return city; | |
| return null; | |
| } | |
| public void setPropertyValue(Object id, Object value) { | |
| if ("name".equals(id)) name = (String)value; | |
| else if ("street".equals(id)) street = (String)value; | |
| else if ("city".equals(id)) city = (String)value; | |
| } | |
| public boolean isPropertySet(Object id) { | |
| return false; | |
| } | |
| public void resetPropertyValue(Object id) { | |
| } | |
| }</pre> | |
| <p>In this example, the object has three properties that are all <em>text</em> | |
| values (one of the property descriptors that defines the behaviour of a | |
| property for the Property view is marked in bold). The first parameter | |
| is the name of the property, and the second one is the label for that | |
| property in the view. There are other types of property descriptors that | |
| you can use; you can even make your own if you have a special type of | |
| property.</p> | |
| <p>This example has been kept deliberately simple. These properties | |
| cannot be reset, nor does the implementation have any notion of whether | |
| or not properties have been set. A more sophisticated implementation | |
| will provide the user with more sophisticated options.</p> | |
| <p>As indicated earlier, this solution does not provide the most | |
| correct implementation possible. With this implementation, the domain | |
| object needs to know about the very view-centric (and Eclipse-centric) | |
| notion of being a property source; in short, there is a tight-coupling | |
| between the model and the property viewing framework. This where | |
| adapters come in.</p> | |
| <h2>Introducing Adapters</h2> | |
| <p>Tight coupling is bad because it tends to make things less | |
| flexible. Sure, we can look at the properties of our domain object, but | |
| what happens when we want to participate in other interactions? Do we | |
| just implement another interface? And another? Tight coupling makes | |
| reuse harder as well. Tightly coupling our domain class with the <code>IPropertySource</code> | |
| interface makes it so that our domain class can't exist without that | |
| interface (and all the other types packaged along with it, plus those | |
| bits referenced by all those types, ...). The an adapter framework | |
| solves this problem by decoupling the domain class from the | |
| view-specific code required to make the Properties view work.</p> | |
| <p>The first step is to remove the <code>IPropertySource</code> | |
| behaviour from the domain class:</p> | |
| <pre>... | |
| public class Person implements <strong>IAdaptable</strong> { | |
| private String name; | |
| private Object street; | |
| private Object city; | |
| public Person(String name) { | |
| this.name = name; | |
| this.street = ""; | |
| this.city = ""; | |
| } | |
| <strong>public Object getAdapter(Class adapter)</strong> { | |
| if (adapter == IPropertySource.class) return new PersonPropertySource(this); | |
| return null; | |
| } | |
| // Getter and setter methods follow... | |
| ... | |
| }</pre> | |
| <p>We move the <code>IPropertySource</code> behaviour to the <code>PersonPropertySource</code> | |
| class:</p> | |
| <pre>... | |
| public class PersonPropertySource implements IPropertySource { | |
| private final Person person; | |
| public PersonPropertySource(Person person) { | |
| this.person = person; | |
| } | |
| public Object getEditableValue() { | |
| return this; | |
| } | |
| public IPropertyDescriptor[] getPropertyDescriptors() { | |
| return new IPropertyDescriptor[] { | |
| new TextPropertyDescriptor("name", "Name"), | |
| new TextPropertyDescriptor("street", "Street"), | |
| new TextPropertyDescriptor("city", "City") | |
| }; | |
| } | |
| public Object getPropertyValue(Object id) { | |
| if ("name".equals(id)) return person.getName(); | |
| else if ("street".equals(id)) return person.getStreet(); | |
| else if ("city".equals(id)) return person.getCity(); | |
| return null; | |
| } | |
| public boolean isPropertySet(Object id) { | |
| return false; | |
| } | |
| public void resetPropertyValue(Object id) { | |
| } | |
| public void setPropertyValue(Object id, Object value) { | |
| if ("name".equals(id)) person.setName((String)value); | |
| else if ("street".equals(id)) person.setStreet((String)value); | |
| else if ("city".equals(id)) person.setCity((String)value); | |
| } | |
| }</pre> | |
| <p>The Properties view goes through a few steps to sort out how it's | |
| going to display properties. First, it determines whether or not the | |
| selected object implements the <code>IPropertySource</code> interface. | |
| If it does (as it does in our first example), it uses the selected | |
| object directly (after casting it to <code>IPropertySource</code>). If | |
| that check fails, the Property view then determines whether or not the | |
| selected object implements the <code>IAdaptable</code> interface | |
| (highlighted in the <code>Person</code> class). If the selected object | |
| is adaptable, it is asked—via the <code>getAdapter</code> | |
| method—for an adapter with the <code>IPropertySource</code> type. | |
| The <code>getAdapter</code> method either returns an object of the | |
| appropriate type or <code>null</code> if it cannot be adapted to the | |
| requested type. If the method returns an adapter, it is used by the | |
| Property view to gather properties (if it is <code>null</code> the | |
| Property view shows nothing).</p> | |
| <p>The astute reader will notice that this really doesn't do very | |
| much to actually weaken the coupling between the domain class and <code>IPropertySource</code> | |
| (the <code>IPropertySource</code> type is referenced directly by the <code>getAdapter</code> | |
| method). In fact, the coupling is just as strong. Even worse, we've | |
| actually introduced a tight coupling to another type (<code>IAdaptable</code>).</p> | |
| <h2>Decoupling with Adapters</h2> | |
| <p>The next step is to completely decouple the domain class from <code>IPropertySource</code>. | |
| To do this, we change the <code>getAdapter</code> method:</p> | |
| <pre>public class Person implements IAdaptable { | |
| private String name; | |
| private Object street; | |
| private Object city; | |
| public Person(String name) { | |
| this.name = name; | |
| this.street = ""; | |
| this.city = ""; | |
| } | |
| <strong>public Object getAdapter(Class adapter) { | |
| return Platform.getAdapterManager().getAdapter(this, adapter); | |
| }</strong> | |
| ... | |
| }</pre> | |
| <p>Previously, the <code>getAdapter</code> method checked the type | |
| of the desired adapter and created an appropriate instance (if possible) | |
| itself. Now, the method makes a call to the <code>AdapterManager</code>, | |
| which can take care of figuring out how to adapt the instance.</p> | |
| <p>For this to work, the adapter manager needs to be told how to | |
| adapt the type. This can be done declaratively through the <code>plugin.xml</code> | |
| file using the <code>org.eclipse.core.runtime.adapters</code> extension | |
| point:</p> | |
| <pre><plugin> | |
| <extension | |
| point="org.eclipse.core.runtime.adapters"> | |
| <factory | |
| adaptableType="org.eclipse.articles.adapters.core.Person" | |
| class="org.eclipse.articles.adapters.properties.PersonPropertiesSourceAdapterFactory"> | |
| <adapter | |
| type="org.eclipse.ui.views.properties.IPropertySource"> | |
| </adapter> | |
| </factory> | |
| </extension> | |
| </plugin></pre> | |
| <p>This extension defines an adapter for instances of the <code>org.eclipse.articles.adapters.core.Person</code> | |
| class. When asked to adapt to the <code>org.eclipse.ui.views.properties.IPropertySource</code>, | |
| the <code>PersonPropertiesSourceAdapterFactory</code> is used. This | |
| factory class is defined as such:</p> | |
| <pre>public class PersonPropertiesSourceAdapterFactory implements IAdapterFactory { | |
| public Object getAdapter(Object adaptableObject, Class adapterType) { | |
| if (adapterType == IPropertySource.class) | |
| return new PersonPropertySource((Person)adaptableObject); | |
| return null; | |
| } | |
| public Class[] getAdapterList() { | |
| return new Class[] {IPropertySource.class}; | |
| } | |
| }</pre> | |
| <p>The <code>getAdapter</code> method does the heavy lifting and | |
| creates the adapter. The <code>getAdapterList</code> returns a list of | |
| the types of adapters the factory can create.</p> | |
| <p>Adapter factories can also be registered programmatically using | |
| APIs on the <code>AdapterManager</code> class.</p> | |
| <p>The Properties view, and other users of the adapter framework | |
| actually go through three steps to find an adapter:</p> | |
| <ol> | |
| <li>If the object implements the required interface, use the | |
| object</li> | |
| <li>If the object implements <code>IAdaptable</code>, call the <code>getAdapter</code> | |
| method and use the returned object; if something other than <code>null</code> | |
| is answered, use the returned value</li> | |
| <li>Get the <code>AdapterManager</code> to try and adapt the | |
| object</li> | |
| </ol> | |
| <p>The final step actually makes the current implementation of <code>getAdapter</code> | |
| in the <code>Person</code> class redundant. We can simply remove it, and | |
| remove the reference to the <code>IAdaptable</code> interface from the | |
| class. That is, the <code>Person</code> class simplifies to:</p> | |
| <pre>public class Person { | |
| private String name; | |
| private Object street; | |
| private Object city; | |
| public Person(String name) { | |
| this.name = name; | |
| this.street = ""; | |
| this.city = ""; | |
| } | |
| ... | |
| }</pre> | |
| <p>Voila! the domain class is totally decoupled from the adapter | |
| type. Instances of the person class, when selected in your favourite | |
| view, will populate the Properties view.</p> | |
| <a name="code"></a> | |
| <h3>Get the code</h3> | |
| <p>This example can be added to your Eclipse development environment | |
| either by downloading and importing the Plug-in Projects contained in <a | |
| href="code/adapters.zip">adapters.zip</a>. Alternatively, you can pull | |
| the projects directly out of Eclipse CVS by importing the <a | |
| href="code/adapters.psf">Team Project Set</a>. Note that you'll need to | |
| have support for the <a href="http://www.eclipse.org/pde">Plug-in | |
| Development Environment</a> (PDE) included with your Eclipse configuration. | |
| You can ensure that you have this functionality available by downloading | |
| either the <em>Eclipse Classic</em>, or the <em>Eclipse for | |
| Plug-in/RCP Developers</em> package. These bundles were created using the | |
| Ganymede release of Eclipse, which contains version 3.4 of the Eclipse | |
| Platform.</p> | |
| <p>The example includes three different bundles:</p> | |
| <dl> | |
| <dt><code>org.eclipse.articles.adapters.core</code></dt> | |
| <dd> | |
| <p>The "core" bundle contains the <code>Person</code> | |
| class and nothing else. It has no dependencies on any other bundles.</p> | |
| </dd> | |
| <dt><code>org.eclipse.articles.adapters.properties</code></dt> | |
| <dd> | |
| <p>The "properties" bundle contains the <code>PersonPropertiesSourceAdapterFactory</code> | |
| and <code>PersonPropertiesSource</code> classes. It also has a | |
| plugin.xml file which defines the extension to <code>org.eclipse.core.runtime.adapters</code> | |
| that registers the adapter.</p> | |
| <p>This bundle further includes a bundle activator that implements | |
| <code>org.eclipse.ui.IStartup</code> to participate in early startup | |
| (via the <code>org.eclipse.ui.startup</code> extension point). The | |
| properties view uses the <code>IAdapterManager#getAdapter(Object, | |
| Class)</code> method which—as discussed later in this | |
| document—only finds adapters from bundles that are activated.</p> | |
| </dd> | |
| <dt><code>org.eclipse.articles.adapters.ui</code></dt> | |
| <dd> | |
| <p>The "ui" bundle defines a very simple view that | |
| displays a list of <code>Person</code> objects. When an object is | |
| selected in this view, the Properties view (if it is open) will show | |
| the properties of the selection.</p> | |
| </dd> | |
| </dl> | |
| <p>There are several important aspects to this example. First, the | |
| "core" bundle is written without any explicit or implicit | |
| dependencies on anything that is part of the Eclipse framework. This | |
| bundle can therefore be reused easily in other applications without | |
| modification. It's also important that the view defined in the | |
| "ui" component has no dependencies on the Properties view. | |
| These two views integrate completely on the workbench, yet know | |
| absolutely nothing about each other. It is the "properties" | |
| bundle that provides the glue between the two.</p> | |
| <h2>Adapting</h2> | |
| <p>The adapter framework is great for getting your objects to | |
| tightly integrate with existing parts of the Eclipse infrastructure, | |
| while remaining loosely coupled with the actual implementation. Loose | |
| coupling with tight integration is pretty powerful stuff.</p> | |
| <p>The <em>"Image Preview" view</em>, is example plug-in that | |
| displays the image (if one is available) for a file selected in the | |
| workbench. The best part is that the view is completely decoupled from | |
| the Resources API. That is, it doesn't know anything about files, | |
| directories, workspaces, or anything else in particular. The view is | |
| shown in action below:</p> | |
| <div class="figure"><img src="images/image-viewer.png" /></div> | |
| <p>The code for the <a href="http://www.eclipse.org/examples/imageviewer">Image Preview example</a> | |
| can accessed via the <a href="http://www.eclipse.org/examples">Eclipse Examples project</a>.</p> | |
| <p>By implementing the Image Preview view using adapters, it can be | |
| easily made to display an image for different kinds of selected objects | |
| by providing a new adapter. A natural extension of this is that we can | |
| use the Image Preview view in a <a href="http://www.eclipse.org/rcp">Rich | |
| Client Platform</a> (RCP) application to display an image for arbitrary | |
| domain objects (assuming that it makes sense for the objects to provide | |
| images, of course).</p> | |
| <p>The Image Preview view listens to the workbench selection | |
| service. When a selection occurs in a view (such as the Package | |
| Explorer, or Navigator), the selection service notifies registered | |
| listeners. When the Image Preview view is notified of the selection | |
| change, it attempts to adapt the selected object to the <code>ImageProvider</code> | |
| interface (which is defined in the same bundle as the view). The <code>ImageProvider</code> | |
| is then used to obtain an image.</p> | |
| <p>Recall the three steps to find an adapter:</p> | |
| <ol> | |
| <li>If the selected object implements the <code>ImageProvider</code> | |
| interface, use the object</li> | |
| <li>If the object implements <code>IAdaptable</code>, call the <code>getAdapter</code> | |
| method asking for an <code>ImageProvider</code>; if something other | |
| than <code>null</code> is answered, use the returned value</li> | |
| <li>Get the <code>AdapterManager</code> to try and adapt the | |
| object</li> | |
| </ol> | |
| <p>The <code>getImageProvider</code> method in the Image Preview | |
| view looks like this:</p> | |
| <pre>private ImageProvider getImageProvider(Object object) { | |
| // First, if the object is an ImageProvider, use it. | |
| if (ImageProvider.class.isInstance(object)) return (ImageProvider)object; | |
| // Second, if the object is adaptable, ask it to get an adapter. | |
| ImageProvider provider = null; | |
| if (object instanceof IAdaptable) | |
| provider = (ImageProvider)((IAdaptable)object).getAdapter(ImageProvider.class); | |
| // If we haven't found an adapter yet, try asking the AdapterManager. | |
| if (provider == null) | |
| provider = (ImageProvider)Platform.getAdapterManager().<strong>loadAdapter</strong>(object, ImageProvider.class.getName()); | |
| return provider; | |
| }</pre> | |
| <p>The first step, is to see if the selected object implements the | |
| required interface. If it does, we cast and return it. If we make it to | |
| the second step, we ask the object if it is adaptable (i.e. does it | |
| implement the <code>IAdaptable</code> interface?). If it does, we use | |
| that method to attempt to find an adapter. If that method fails (returns | |
| <code>null</code>), the <code>AdapterManager</code> is used. Ultimately, | |
| this method may fail to find an appropriate adapter and return <code>null</code>.</p> | |
| <p>The selected object doesn't need to know anything about the Image | |
| Preview view. Conversely, the Image Preview view knows nothing about | |
| files or any other particular type of object. The adapter implementor | |
| knows about both (it really only needs to know about the <code>ImageProvider</code> | |
| interface).</p> | |
| <p>Note that there are two different ways to ask the <code>AdapterManager</code> | |
| to adapt an object: <code>getAdapter</code> or <code>loadAdapter</code> | |
| (which is highlighted in the snippet). Both methods will find | |
| programmatically- and declaratively-registered adapters, however the <code>getAdapter</code> | |
| method will only find declaratively-registered adapters <em>if the | |
| bundle that contributes them has already been activated</em>. The <code>loadAdapter</code> | |
| will load and activate the bundle (if required) as part of the process.</p> | |
| <h2>Conclusion</h2> | |
| <p>The adapter framework is a important part of every Eclipse plug-in developer's | |
| arsenal. By using the adapter framework, you can easily create bundles that have no | |
| explicit coupling to the Eclipse platform. The adapter framework can be used to | |
| significantly reduce the coupling in an application, encouraging higher-quality code | |
| and making it generally easier to understand and reuse parts of the application. | |
| Tight integration with loose coupling is a powerful concept.</p> | |
| </div> | |
| </body> | |
| </html> |