Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 321209

Summary: [DataBinding] [JFace] setting ObservableListContentProvider on ComboViewer twice throws NPE
Product: [Eclipse Project] Platform Reporter: Henno Vermeulen <strider80>
Component: UIAssignee: Matthew Hall <qualidafial>
Status: RESOLVED WONTFIX QA Contact: Matthew Hall <qualidafial>
Severity: minor    
Priority: P3 CC: tango17
Version: 4.0   
Target Milestone: ---   
Hardware: PC   
OS: Windows 7   
Whiteboard:
Attachments:
Description Flags
Self contained snippet which shows Bug 321209 none

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.