| <!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 9, 2008</div> |
| </div> |
| |
| <div class="content"> |
| <h2>Using the Properties View</h2> |
| |
| <p>This article is not intended to provide thorough coverage of the |
| Properties view. Rather, the Properties view is used a a means for |
| demonstrating how adapters can be put to work.</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 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 |
| the property in 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 is "not necessarily <em>[the]</em> |
| most correct". This is because, for this to work, 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, ...).</p> |
| |
| <p>Eclipse provides an adapter framework that can be used to solve |
| 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 AdapterManager.getDefault().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.example.Person" |
| class="org.eclipse.example.adapters.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.example.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>With the adapters registered, we can make one more |
| change to the code. When adapters are used, they are usually used in |
| three steps:</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> |
| |
| <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 Image Preview view can accessed via the Eclipse Evangelism |
| site:</p> |
| |
| <pre><a href="http://www.eclipse.org/evangelism/samples/imageviewer/">http://www.eclipse.org/evangelism/samples/imageviewer/</a></p></pre> |
| |
| <p> By |
| implementing the image viewer 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 my domain objects (assuming that |
| this makes sense, 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 already |
| implements our 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, my 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></p> |
| </div> |
| </body> |
| </html> |