Bug 416339 - [JFace] The used input types for the generic JFace API are too restrictive!
Summary: [JFace] The used input types for the generic JFace API are too restrictive!
Status: CLOSED WONTFIX
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 4.4   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Lars Vogel CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords:
Depends on:
Blocks: 402445
  Show dependency tree
 
Reported: 2013-09-02 05:43 EDT by Hendrik Still CLA
Modified: 2020-07-30 11:59 EDT (History)
5 users (show)

See Also:


Attachments
Example for incorrect generics implementation (2.75 KB, text/plain)
2013-09-04 09:46 EDT, Lars Vogel CLA
no flags Details
Sample Class to describe the compile error (382 bytes, text/x-java)
2013-09-09 03:27 EDT, Hendrik Still CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Hendrik Still CLA 2013-09-02 05:43:09 EDT
For example the input parameter type of
setContentProvider(IContentProvider<I>) should be IContentProvider<?
extends I>  to allow clients to set subtypes of IContentProvider<I> as
ContentProvider.
This would allow a more flexible use for the client.
Comment 1 Hendrik Still CLA 2013-09-03 06:52:45 EDT
The usage of extends is not that useful in this case.
An upper bounded wildcard would allow to set a more specific ContentProvider, which could possibly not handle the input of the type I.
For example we change the signature to setContentProvider(IContentProvider<? extends I> cp).
Now create a Viewer which handles every Object as input TableViewer<XYZ,Object>.
With the upper bounded wildcard, it is now possible so set a ContentProvider which handles a subtype of Object.
tableViewer.setContentProvider(new IContentProvider<String>(){});

This is wrong, because now we can set an Object as input which can not be handled my the assigned "String ContentProvider"


So to be less restrictive we should use lower bounded wildcards.
With the Signature setContentProvider(IContentProvider<? super I> cp)
it is possible to assign ContentProvider which can handle input from the type I or its super types.

For example with the viewer TableViewer<String,MyDAO>() it is now possible to set a ContentProvider which handles input of every type.
tableViewer.setContentProvider(new IContentProvider<Object>(){});
Comment 2 Lars Vogel CLA 2013-09-04 09:46:51 EDT
Created attachment 235148 [details]
Example for incorrect generics implementation

I agree with Hendriks analysis. If we could use void setContentProvider(IContentProvider<? extends I> provider) on StructuredViewer we could not avoid runtime failures. I attach an example for such an incorrect behavior. 

setContentProvider(IContentProvider<? super I> provider) avoids IMHO CCE.
Comment 3 Lars Vogel CLA 2013-09-04 09:48:57 EDT
Adding Ed as he suggested <? extends I> for the content provider. @Ed we think <? super I> is the correct implementation.
Comment 4 Ed Merks CLA 2013-09-05 05:45:08 EDT
Yes, I see what you mean!  It's more like java.util.Collections.sort(List<T>, Comparator<? super T>) where you have to ensure that the comparator can properly receive any of the list's elements...
Comment 5 Hendrik Still CLA 2013-09-09 03:26:47 EDT
Currently it is possible to use the <? super I> type only with a casting. 
My idea was to also use the super-type not only as input type for the setContentProvider method, but also as type for the field contentProvider in the ContentViewer. But this leads to compiler error, which I don't really understand. 
If the contentProvider field is typed as <? super I> the following line will not compile:
contentProvider.inputChanged(this, getInput(), null);

With the following error message:
The method inputChanged(Viewer<capture#3-of ? super I>, capture#3-of ? super I, capture#3-of ? super I) in the type IContentProvider<capture#3-of ? super I> is not applicable for the arguments (ContentViewer<E,I>, I, null)

I tracked the error down with the sample class I'll attache.
Comment 6 Hendrik Still CLA 2013-09-09 03:27:54 EDT
Created attachment 235293 [details]
Sample Class to describe the compile error
Comment 7 Sebastian Zarnekow CLA 2013-09-09 03:52:37 EDT
(In reply to Hendrik Still from comment #5)
> Currently it is possible to use the <? super I> type only with a casting. 
> My idea was to also use the super-type not only as input type for the
> setContentProvider method, but also as type for the field contentProvider in
> the ContentViewer. But this leads to compiler error, which I don't really
> understand. 

The problem is your provider interface:

public class Provider<I> {
  void doSomeThing(List<I> inData){}
}

It expects exactly a list that promises to accept elements of type I and to return elements of type I. In other words, the compiler wants to ensure that implementations of that interface may read and write the the 'inData' without type propblems. Now consider that the type variable I is bound to '? super T' which is not concrete but only an existential type, e.g. the compiler will only ensure that this is some type which exists and which is a super type of T. Based on that, it cannot be proven that the List of T is not used in an invalid manner. To give you an example on what happens if you use a cast:

class ProviderContainer<T>{
	Provider<? super T> provider;
	public void setProvider(Provider<? super T> provider) {
		this.provider = provider;
	}
	void doSomeThingWithTheProvider(List<T> inData){
// cast here
		 ((Provider<T>)this.provider).doSomeThing(inData);
	}
	public static class Provider<I> {
		void doSomeThing(List<I> inData){
			System.out.println(inData);
		}
	}
}

// custom provider implementation that sticks to the
// contract of Provider<T>
class MyProvider extends ProviderContainer.Provider<CharSequence> {
	@Override
	void doSomeThing(List<CharSequence> inData) {
// we add a StringBuilder to the list of char sequences
		inData.add(0, new StringBuilder());
	}
}


class MyProviderContainer extends ProviderContainer<String> {
	@Override
	void doSomeThingWithTheProvider(List<String> inData) {
		super.doSomeThingWithTheProvider(inData);
// caboom in the next line
// since we read from the list as String but the provider
// legally added a string builder
		String first = inData.get(0); 
	}
	
	public static void main(String[] args) {
		MyProviderContainer container = new MyProviderContainer();
		container.setProvider(new MyProvider());
		container.doSomeThingWithTheProvider(new ArrayList<String>());
	}
}


A better way to solve the compile problem is to think about the contract of a provider, e.g. it could be worthwhile to only read from the the list and thereby
the API should only guarantee that readers will get some instance of I and disallowing writer to alter the list, e.g to doSomething(List<? extend I> inData) is compilable and will prevent CCE due to valid client code.
Comment 8 Hendrik Still CLA 2013-09-09 05:04:24 EDT
Thank you Sebastian for your really quick answer.
I had to think more than twice about it.
Thank you very much.
Comment 9 John Arthorne CLA 2013-09-16 09:54:20 EDT
I have merged Hendrik's fix into the generics branch so we can try it out on real world examples:

http://git.eclipse.org/c/platform/eclipse.platform.ui.git/log/?h=johna/402445
Comment 10 John Arthorne CLA 2013-09-20 09:18:07 EDT
There was a small additional fix to some synchronization code in Hendrik's previous fix:

http://git.eclipse.org/c/platform/eclipse.platform.ui.git/commit/?h=johna/402445&id=4821302fabf4f316b32d55acd083484863a9ce3f
Comment 11 Lars Vogel CLA 2015-08-14 11:26:52 EDT
Reopen for investigation for migration to master for Neon.
Comment 12 Eclipse Genie CLA 2020-07-30 11:59:53 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. As such, we're closing this bug.

If you have further information on the current state of the bug, please add it and reopen this bug. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.