Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 321209 - [DataBinding] [JFace] setting ObservableListContentProvider on ComboViewer twice throws NPE
Summary: [DataBinding] [JFace] setting ObservableListContentProvider on ComboViewer t...
Status: RESOLVED WONTFIX
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 4.0   Edit
Hardware: PC Windows 7
: P3 minor (vote)
Target Milestone: ---   Edit
Assignee: Matthew Hall CLA
QA Contact: Matthew Hall CLA
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-07-29 06:43 EDT by Henno Vermeulen CLA
Modified: 2010-09-03 14:18 EDT (History)
1 user (show)

See Also:


Attachments
Self contained snippet which shows Bug 321209 (3.88 KB, application/octet-stream)
2010-07-29 06:46 EDT, Henno Vermeulen CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Henno Vermeulen CLA 2010-07-29 06:43:30 EDT
Build Identifier: 20090920-1017

When calling
viewer.setContentProvider(viewer.getContentProvider())
on a ComboViewer that contains an ObservableListContentProvider a NullPointerException is thrown in a jface databinding internal class.

Even worse, when trying to decorate the contentprovider by adding an element that represents a null value, I get the same exception.

Workaround: set the right IStructuredContentProvider once.

Exception in thread "main" java.lang.NullPointerException
	at org.eclipse.jface.internal.databinding.viewers.ObservableCollectionContentProvider.setViewer(ObservableCollectionContentProvider.java:162)
	at org.eclipse.jface.internal.databinding.viewers.ObservableCollectionContentProvider.inputChanged(ObservableCollectionContentProvider.java:155)
	at org.eclipse.jface.databinding.viewers.ObservableListContentProvider$Impl.inputChanged(ObservableListContentProvider.java:57)
	at org.eclipse.jface.databinding.viewers.ObservableListContentProvider.inputChanged(ObservableListContentProvider.java:171)
	at org.eclipse.jface.snippets.viewers.SnippetComboViewerBug$NullElementStructuredContentProviderDecorator.inputChanged(SnippetComboViewerBug.java:85)
	at org.eclipse.jface.viewers.ContentViewer.setContentProvider(ContentViewer.java:251)
	at org.eclipse.jface.viewers.StructuredViewer.setContentProvider(StructuredViewer.java:1611)
	at org.eclipse.jface.snippets.viewers.SnippetComboViewerBug.createPartControl(SnippetComboViewerBug.java:105)
	at org.eclipse.jface.snippets.viewers.SnippetComboViewerBug$1.run(SnippetComboViewerBug.java:120)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332)
	at org.eclipse.jface.snippets.viewers.SnippetComboViewerBug.main(SnippetComboViewerBug.java:115)

Reproducible: Always

Steps to Reproduce:
1. Create a ComboViewer with an ObservableListContentProvider and some input.
2. Call viewer.setContentProvider(viewer.getContentProvider())

Alternatively, run the attached snippet.
Comment 1 Henno Vermeulen CLA 2010-07-29 06:46:42 EDT
Created attachment 175477 [details]
Self contained snippet which shows Bug 321209
Comment 2 Matthew Hall CLA 2010-08-23 01:18:19 EDT
SlowStrider: the problem is that when you set a new content provider on a ContentViewer, the viewer disposes the old IContentProvider before setting up the new one.  However you're wrapping the old content provider in the new one, so by the time the new one is getting initialized, the provider it wraps is disposed and no longer safe to use.

    viewer.setContentProvider(new ObservableListContentProvider());
    viewer.setInput(new WritableList(Arrays.asList("male", "female"),
        String.class));
    // Throws a NullPointerException in a jface databinding internal class.
    // Workaround: set the decorated content provider when first
    // calling viewer.setContentProvider.
    viewer.setContentProvider(
        new NullElementStructuredContentProviderDecorator(
            (IStructuredContentProvider) viewer.getContentProvider()));

A safer workaround is to just use a new Observable<List|Set>ContentProvider instance each time you change the content provider:

    viewer.setContentProvider(
        new NullElementStructuredContentProviderDecorator(
            new ObservableListContentProvider()));

And this will definitely break with the DataBinding content providers:

    viewer.setContentProvider(viewer.getContentProvider());

I've added documentation to the Observable(List|Set)(Tree)?ContentProvider.dispose() methods explaining that they become unusable after disposal, and that the viewer disposes replaced content providers.