This item provider implementation is a convenient reusable base
that can be used for an item provider that isn't an adapter for an EMF object.
This default implementation is highly functional and is plastic enough for a wide variety of uses
(as will be illustrated in the examples to come).
The plasticity is the reason for providing a huge number of constructors.
The
#children list is implemented using
ItemProviderNotifyingArrayList.
As a result, any modification of the collection (using the standard
java.util.List interface)
will automatically fire the correct call to each
INotifyChangedListener in the
#changeNotifier.
Furthermore,
IUpdateableItemParent#setParent
is called to update
#parent for the objects that are added to or removed from the list,
but optionally, i.e., only if the interface is implemented---the
#adapterFactory is used if it isn't null.
There is also a
#text and an
#image,
which can be set via
#setText(String) and
#setImage(Object)to cause appropriate domain event notifications to be fired.
The set methods use the stateless adapter signature for uniformity and to support
IUpdateableItemText#setText(Object,String).
This class is useful as a convenient wrapper object to act as the input to a view, e.g.,
viewer.setInput(new ItemProvider(text, image, collection));
lets you take a mixed collection of model objects and item providers,
and show it as the elements of a structured view, i.e., as the visible roots of the view.
Although a structured viewer
does not show it's input object within the view,
it
does show the input object on the pane title.
The above pattern allows you to inject a collection or the object itself into the structured viewer
and to control the pane title at the same time, e.g.,
viewer.setInput(new ItemProvider(Collections.singleton(object)));
will leave the pane title blank and show the object as the root of the structured view.
One could use more of these item providers to build up a scaffolding within views.
Consider the following block of code
which has access to a collection of
INotifyChangedListeners.
// final Collection listeners = ...;
// final StructuredContentViewer contentViewer = ...;
//
// These create the items and build up the structure.
//
final ItemProvider child11 = new ItemProvider(listeners, "Child 1");
final ItemProvider child12 = new ItemProvider(listeners, "Child 2");
final ItemProvider parent1 = new ItemProvider(listeners, "Parent 1", Arrays.asList(new Object [] {child11, child12}));
final ItemProvider child21 = new ItemProvider(listeners, "Child 1");
final ItemProvider child22 = new ItemProvider(listeners, "Child 2");
final ItemProvider parent2 = new ItemProvider(listeners, "Parent 2", Arrays.asList(new Object [] {child21, child22}));
final ItemProvider grandParent = new ItemProvider(listeners, "Grand Parent", Arrays.asList(new Object [] {parent1, parent2}));
// Set the items into the visible roots of the structured content viewer.
//
contentViewer.setInput(new ItemProvider("Pane Tile", Collections.singleton(grandParent)));
// Create some delayed actions that modify the item structure.
//
if (contentViewer.isControlOkToUse())
{
contentViewer.getControl().getDisplay().asyncExec
(new Runnable()
{
public void run()
{
// Use standard list modification that has the effect of producing a domain event notification.
//
parent1.getChildren().removeAll(Arrays.asList(new Object [] {child11, child12}));
contentViewer.getControl().getDisplay().asyncExec
(new Runnable()
{
public void run()
{
// This also as the effect of producing a correct a domain event notification.
//
parent2.setText("Parent 2!");
}
});
}
});
}
The structure will be displayed within the contentViewer and will then change a little bit later;
the flickering should be noticeable if the viewer is set to auto expand.
Another common pattern of usage will be to inject scaffolding within an EMF structure.
In the following example, a new factory is defined to replace the adapters for Company and Department
so as to inject an item that acts as the child of the Company and the parent of each Department.
(Normally, this would not be done with all these inner classes.)
ItemProviderAdapterFactory myItemProviderAdapterFactory =
new ItemProviderAdapterFactory()
{
public Adapter createCompanyAdapter()
{
// This returns a new instance each time.
// The instance stores an injected child that in turn will have this original object's children as its children.
//
return
new CompanyItemProvider(this)
{
// Keep track of the new child added below company.
//
ItemProvider injectedChild;
public Collection getChildren(final Object object)
{
// Create one on demand.
//
if (injectedChild == null)
{
injectedChild =
(new ItemProvider("Injected Child")
{
public Collection getChildren(Object o)
{
// Return the department of the company.
// Note that we ignore o in favour of object.
//
return ((Company)object).getDepartment();
}
public boolean hasChildren(Object o)
{
// You have to make sure you override this method to match the above.
//
return !((Company)object).getDepartment().isEmpty();
}
});
}
return Collections.singleton(injectedChild);
}
public boolean hasChildren(Object object)
{
// You have to make sure you override this method to match the above.
//
return true;
}
public void notifyChanged(Notification msg)
{
// If the departments are affected...
//
Company company = (Company)msg.getNotifier();
if (msg.getStructuralFeature() == company.ePackageCompany().getCompany_Deparment())
{
// If there's a child around to care...
//
if (injectedChild != null)
{
// Fire the domain event as if it came from the child.
//
// EATM TODO
fireNotifyChanged(injectedChild, msg.getEventType(), msg.getStructuralFeature(), msg.getOldValue(), msg.getNewValue(), msg.getPostition());
}
}
else
{
// Behave as normal.
//
super.notifyChanged(msg);
}
}
};
}
public Adapter createDepartmentAdapter()
{
// This is still stateless.
//
if (departmentItemProvider == null)
{
departmentItemProvider =
new DepartmentItemProvider(this)
{
public Object getParent(Object object)
{
// Use the stateful adapter of the containing parent to determine the injected item.
//
Company company = ((Department)object).getCompany();
ITreeItemContentProvider companyAdapter =
(ITreeItemContentProvider)this.adapterFactory.adapt(company, ITreeItemContentProvider.class);
if (companyAdapter != null)
{
// Get the first child of the company's adapter.
//
return companyAdapter.getChildren(company).iterator().next();
}
else
{
return null;
}
}
};
}
// Return the single factory instance.
//
return departmentItemProvider;
}
};