Time for action – showing properties

Instead of every object having to have its own custom information dialog, the Eclipse IDE provides a generic Properties view (in the org.eclipse.ui.views plug-in), which can be used to show information about the currently selected object. The properties are discovered generically from an object and accessed through the IPropertySource interface. This allows an object to provide an abstracted way of computing the fields shown in the property view.

The easiest way to create a property source is to let the object in question implement its own IPropertySource interface. This works when the source code can be modified, but in many cases (such as the TimeZone, or a Map.Entry containing a String key and a TimeZone) the source code cannot be modified.

  1. Open the MANIFEST/META-INF.MF of the plug-in, and add org.eclipse.ui.views as a dependency via the Dependencies tab, or by adding it to the bundles in the Require-Bundle entry. Without this, the IPropertySource interface won't be found.
  2. Create a class TimeZonePropertySource in the com.packtpub.e4.clock.ui.internal package that implements the IPropertySource interface. Take a single TimeZone instance in the constructor.
    public class TimeZonePropertySource implements IPropertySource {
      private TimeZone timeZone;
      public TimeZonePropertySource(TimeZone timeZone) {
       this.timeZone = timeZone;
      }
    }
  3. The only methods that need to be implemented are getPropertyValue() and getPropertyDescriptors(). (The other methods, such as getEditableValue() and isPropertySet(), can be ignored, because they only get invoked when performing edit operations. These should be left empty or return null/false.) The accessors are called with an identifier; while the latter returns an array of PropertyDescriptors combining pairs of identifiers and a displayable name. Add the following to the TimeZonePropertySource class:
    private static final Object ID = new Object();
    private static final Object DAYLIGHT = new Object();
    private static final Object NAME = new Object();
    public IPropertyDescriptor[] getPropertyDescriptors() {
      return new IPropertyDescriptor[] {
        new PropertyDescriptor(ID, "Time Zone"),
        new PropertyDescriptor(DAYLIGHT, "Daylight Savings"),
        new PropertyDescriptor(NAME, "Name")
      };
    }
    public Object getPropertyValue(Object id) {
      if (ID.equals(id)) {
        return timeZone.getID();
      } else if(DAYLIGHT.equals(id)) {
        return timeZone.inDaylightTime(new Date());
      } else if (NAME.equals(id)) {
        return timeZone.getDisplayName();
      } else {
        return null;
      }
    }
  4. To hook this property source into the Properties view, an adapter is used. It can be specified via a generic IAdaptable interface, which allows a class to virtually implement an interface. Since the TimeZone cannot implement the IAdaptable interface directly, an IAdapterFactory is needed.
  5. Create TimeZoneAdapterFactory in the com.packtpub.e4.clock.ui.internal package that implements the IAdapterFactory interface.
    public class TimeZoneAdapterFactory implements IAdapterFactory {
      public Class[] getAdapterList() {
        return new Class[] { IPropertySource.class };
      }
      public Object getAdapter(Object o, Class type) {
        if(type == IPropertySource.class && o instanceof TimeZone) {
          return new TimeZonePropertySource((TimeZone)o);
        } else {
         return null;
        }
      }
    }
  6. To register the adaptor factory with Eclipse, add it to the plugin.xml file declaratively.
    <extension point="org.eclipse.core.runtime.adapters">
      <factory adaptableType="java.util.TimeZone"
        class="com.packtpub.e4.clock.ui.internal.TimeZoneAdapterFactory">
        <adapter type="org.eclipse.ui.views.properties.IPropertySource"/>
      </factory>
    </extension>
  7. Run the Eclipse instance, select a time zone in the tree view, and then open the properties by navigating to Window | Show View | Other | General | Properties. Nothing will be shown. To confirm that the adapter has been wired correctly, add this at the end of the createPartControl() method in the TimeZoneTreeView view creation:
    System.out.println("Adapter is " + Platform.getAdapterManager().
      getAdapter(TimeZone.getDefault(),IPropertySource.class));
  8. Run the Eclipse instance, open the Time Zone Tree View, and check the Console view of the host Eclipse instance. The console should contain an output message similar to the following:
    Adapter is com.packtpub.e4.clock.ui.internal.TimeZonePropertySource@7f8a6fb0
  9. So what's the missing link? It turns out in this case that the Properties view is not being notified of the change in selection. Adding the following to the createPartControl() method of the TimeZoneTreeView class will solve the problem.
    //The following  commented code needs to be removed
    /*System.out.println("Adapter is " + Platform.getAdapterManager().
       getAdapter(TimeZone.getDefault(),IPropertySource.class));*/
    getSite().setSelectionProvider(treeViewer);
  10. Now, changes in selection are propagated up to the workbench, so that other views can update themselves. When a TimeZone is selected, the Properties, view will be updated automatically. Run the Eclipse instance and open the Time Zone Tree View, select a time zone, and open the Properties view:
    Time for action – showing properties

Note

Time for action – showing properties E4: To hook a viewer up to the selection provider, the code looks similar to this:

@Inject
ESelectionService selectionService;
ISelectionChangedListener selectionListener;
@PostConstruct
public void postConstruct() {
  selectionListener = new ISelectionChangedListener() {
    public void selectionChanged(SelectionChangedEvent e) {
      if (selectionService != null)
        selectionService.setSelection(e.getSelection());
    }
  };
  treeViewer.addSelectionChangedListener(selectionListener);
}
@PreDestroy
public void preDestroy() {
  if(selectionListener != null)
    treeViewer.removeSelectionChangedListener
    (selectionListener);
  selectionListener = null;
}

What just happened?

To update the state of the Workbench's selection, the view's selection provider was connected with that of the page (via the getSite() method). When the selection in the viewer changes, it sends a message to registered listeners of the page's selection service so that they can update their views, if necessary.

Note

What just happened? E4: The selection listener needs to be (un)registered manually to provide a hook between the viewer and the selection service. Instead of being ISelectionService, it's ESelectionService. The interface is slightly different, because the ISelectionService is tied to the IWorkbenchPart class, but the ESelectionService is not.

To provide information to the Properties view, an IPropertySource was created for the TimeZone and associated with the Platform IAdapterManager through the declaration in the plugin.xml file.

It's generally better to provide hooks declaratively in the plugin.xml file rather than hooking it up with the start() and stop() activator methods. That's because the start on the Activator may not be called until the first class is loaded from the bundle; in the case of the adaptor, the declarative registration can provide the information before it is first required.

The adaptor factory provides the getAdapter() method, which wraps or converts the object being passed into one of the desired type. If the object is already an instance of the given type, it can just be returned as it is—but otherwise return a POJO, proxy, or wrapper object that implements the desired interface. It's quite common to have a class (such as TimeZonePropertySupport) whose sole job is to implement the desired interface, and which wraps an instance of the object (TimeZone) to provide the functionality.

The IPropertySupport interface provides a basic means to acquire properties from the object, and to do so it uses an identifier for each property. These can be any object type; in the preceding example, new Object instances were used. Although it is possible to use String (plenty of other examples do), this is not recommended, since the value of the String has no importance and it takes up space in the JVM's PermGen memory space. In addition, using a plain Object means that the instance can be compared with == without any concerns, whereas doing so with String is likely to fail the code reviews or automated style checkers. (The preceding example uses the .equals() method to encourage its use when not using an Object, but a decent JIT will in-line it—particularly since the code is sending the message to a static final instance.)

Pop quiz – understanding properties

Q1. How can TableViewer instances respond to a click?

Q2. Why are Dialog subclasses created?

Q3. What are property descriptors?

Q4. How are properties displayed on the Properties view?

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset