SIDEBAR
»
S
I
D
E
B
A
R
«
TokenViewer – Databindings
Feb 1st, 2009 by Eugene Ostroukhov

TokenViewer was submitted to Eclipse.org as Bug#262846

I remember in one past project (financial application) there was a special control – “Lookup”. Basically, it was a label, text entry and a button. User entered some ID (say, employee SSN) the system will look up the employee, show employee first & last names in non-editable entries (to confirm that correct employee was found) and user may enter other values as he needs. The button would open a selection dialog so the user can select the employee by name.

I tried to implement similar functionality (excluding label and button) using token viewer & JFace bindings. I created a new TitleAreaDialog with following createDialogArea method:


        getShell().setText("Databings Test");
        setTitle("Some Dull Data Entry");
        setMessage("Enter all the needed data");
        Composite root = (Composite) super.createDialogArea(parent);</p>

<pre><code>    Composite dataEntry = new Composite(root, SWT.NONE);
    dataEntry.setLayout(new GridLayout(3, true));
    dataEntry.setLayoutData(new GridData(GridData.FILL_BOTH));
    TokenViewer tokenViewer = new TokenViewer(dataEntry);
    tokenViewer.getControl().setLayoutData(
            new GridData(GridData.FILL_HORIZONTAL));
    tokenViewer.setContentProvider(new EmployeeSSNContentProvider());
    tokenViewer.setLabelProvider(new EmployeeLabelProvider());
    tokenViewer.setInput(new EmployeeDAO());

    Text firstNameDisplay = new Text(dataEntry, SWT.READ_ONLY | SWT.BORDER);
    firstNameDisplay.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

    Text lastNameDisplay = new Text(dataEntry, SWT.READ_ONLY | SWT.BORDER);
    lastNameDisplay.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

    createBindings(tokenViewer, firstNameDisplay, lastNameDisplay);

    return root;

This method adds TokenViewer and two read-only text entries. Afterwards it calls “createBindings” to bind TokenViewer selection to those fields.

private DataBindingContext createBindings(TokenViewer tokenViewer,
            Text firstNameDisplay, Text lastNameDisplay) {
        DataBindingContext bindingContext = new DataBindingContext();</p>

<pre><code>    ISWTObservableValue firstNameObservable = SWTObservables
            .observeText(firstNameDisplay);
    ISWTObservableValue lastNameObservable = SWTObservables
            .observeText(lastNameDisplay);

    IViewerObservableValue selected = ViewersObservables
            .observeSingleSelection(tokenViewer);

    IObservableValue fname = BeansObservables.observeDetailValue(selected,
            "firstName", String.class);
    IObservableValue lname = BeansObservables.observeDetailValue(selected,
            "lastName", String.class);

    bindingContext
            .bindValue(firstNameObservable, fname, new UpdateValueStrategy(
                    false, UpdateValueStrategy.POLICY_NEVER), null);
    bindingContext
            .bindValue(lastNameObservable, lname, new UpdateValueStrategy(
                    false, UpdateValueStrategy.POLICY_NEVER), null);
    return bindingContext;
}
</code></pre>

<p>

As you can see, this method uses JFace bindings to bind TokenViewer selection first and last name to corresponding text fields.

When the user opens dialog the fields are empty. When the user begins typing the “wrong” SSN entry will be underlined until correct value is entered:

typingRead-only text fields will get populated once proper SSN is entered:

enteredUser may also use content assist to select employee from list:

ca-popup

 

Implementation details:

  1. Employee - POJO class that supports property change listeners to conform JFace bindings requirements.
  2. EmployeeDAO - DAO class that fetches Employee objects from database or any other source. In this example it returns a list.
  3. EmployeeLabelProvider – returns labels for content assist pop-up. Labels are “ “. It can also return icons and/or descriptions if needed.
  4. EmployeeSSNContentProvider – an instance of the ITokenContentProvider. It’s main responsibility is to transform text to/from object and to fetch instances from DAO. Notable methods:
    
    public Object[] getElements(Object inputElement) {
        return null;
    }
    
    This method will get called when content assist is invoked for empty field. It may return all elements but I chose to return none since it is common in real-world applications that the system will chose not to query the whole database at once

    public String toString(Object object) {
        return ((Employee) object).getSsn();
    }
This method returns the string that corresponds to given model object. I.e. when entering the classname this string would be fully qualified name – not the short name. CA will automatically convert short name to fully qualified one.

    public Object[] getChildren(Object object, String prefix) {
        Object[] elements = ((EmployeeDAO) object).getAllEmployees().toArray();</p>

<pre><code>    Collection&lt;Object&gt; selected = new LinkedList&lt;Object&gt;();
    for (Object employee : elements) {
        if (((Employee) employee).getSsn().startsWith(prefix)) {
            selected.add(employee);
        }
    }
    return selected.toArray();
}

Returns the elements that match given pattern. In real-world application this method would be smarter and more tightly integrated with DAO object so it only queries entries it needs.

    public Object fromString(Object parent, String value) {
        Object[] children = getChildren(parent, value);
        for (Object child : children) {
            if (((Employee) child).getSsn().equals(value)) {
                return child;
            }
        }
        return null;
    }
This method decodes a string to exact object. If this method returns not-null then selection event will be updated.

You can download sample project (that contains updated TokenViewer) here.

TokenViewer – Basics
Jan 29th, 2009 by Eugene Ostroukhov

I created a new viewer that I think may be really useful. I’m writing this post to provide a high-level description. You can use it as follows:

</p>

<p>final TokenViewer tokenViewer = new TokenViewer(root);</p>

<p>tokenViewer.getControl().setLayoutData(</p>

<p>new GridData(GridData.FILL_HORIZONTAL));</p>

<p>tokenViewer.setContentProvider(new BaseWorkbenchContentProvider());</p>

<p>tokenViewer.setLabelProvider(new WorkbenchLabelProvider());</p>

<p>tokenViewer.setInput(workspaceRoot);</p>

<p>

The result is a simple text field with following services:

  1. Content assist.
  2. Object-oriented IStructuredSelection support – if the user enters proper string the selection event is thrown. If user sets selection – string will appear in the text entry.
  3. Easy and familiar programming model – I guess any JFace/Eclipse plugin developer wrote dozens of those label/content providers.

 

TokenViewer working with workspace

TokenViewer working with workspace

This simple API works for the case when text in the text field is the same as a label in the content assist pop-up. But once you want to have different text (i.e. SSN entry may show employee name in the pop-up) you will need something more specific for this task and that is why I defined special content provider (I still hope it is easier to use then regular JFace content assist API):

</p>

<p>public interface ITokenContentProvider extends IStructuredContentProvider {</p>

<p>Object fromString(Object parent, String value);</p>

<p>String toString(Object object);</p>

<p>Object[] getChildren(Object object, String prefix);</p>

<p>}

  • fromString method will return a child of the given parent node that corresponds to a specific string. It may return null.
  • toString will convert given object to its string representation.
  • getChildren tries to find all the elements that correspond to given string.

If the viewer is provided with IStructuredContentProvider (or ITreeContentProvider) it will wrap it with a custom adapter so you can use your existing code.

The label provider is used to decorate the content assist pop-up. I also have a slightly extended label provider interface:

</p>

<p>public interface IDescriptionProvider extends ILabelProvider {</p>

<p>public String getDescription(Object element);</p>

<p>}

This optional extension provides the way to provide extended object descriptions that are shown along the content assist pop-up:

This is also a tokenviewer

This is also a tokenviewer

Note – red squiggles are automatically shown when there is no object that corresponds to a text.

What I’m going to implement next is:

  1. Multiple selection support when token list is separated with some delimeter.
  2. Rich text (HTML?) support both for content assist pop-up and description pop-up.
»  Substance:WordPress   »  Style:Ahren Ahimsa