The reason I’m breaking all of this out is because we have to follow the generic types.
So, once we have the ReverseConverter, the convertToModel(fieldValue, …) method is called.
This is what I can tell from my debugger:
the type of the realConverter is ObjectToNativeSelectConverter, which is a class I wrote and set in my converter factory.
the type of my fieldValue is String.
So why is the ObjectToNativeSelectConverter being called (I also have a StringToNativeSelectConverter) when the fieldValue is a String? This appears to be the source of my ClassCastException because the type of the variable (String) does not match the generic type of the class (Object).
I have a question about converters. There are two kinds, regular converters and reverse converters. The are basically the same thing, only the order of the conversion types is reversed. This presumably allows us to write fewer converters.
However, for converters and reverse converters to be fully swappable, they need to be completely symmetrical. But sometimes I see code like this:
if (presentationType == String.class)
if (presentationType == Date.class)
And sometimes I see code like this:
if (Integer.class.isAssignableFrom(sourceType)
if (Boolean.class.isAssignableFrom(sourceType)
Shouldn’t we always be using isAssignableFrom() for both sourceTypes and presentationTypes in order to be completely symmetrical?
The sub-window/form is brought up when the user want to edit one row from the table
The form is defined by the same item which backs the row in the table
First I call
form.setItemDataSource(tableItem) to set the backing item on the form
Then I call
form.setVisibleItemProperties() to configure the fields (columns in the table) which are actually visible on the form
It turns out that both
form.setItemDataSource(tableItem) and
form.setVisibleItemProperties() call my FieldFactory’s createField() method. The field is being created twice! This can’t be right. A field should only be created once.
My code (in the middle of the recursion) looks like this, which I believe is correct:
[code]
public Object convertToPresentation(NativeSelect value,
Locale locale)
throws com.vaadin.data.util.converter.Converter.ConversionException {
if (value == null)
return null;
return value.getValue();
}
[/code]
I just delegate my call to the actual object’s getValue() method. The problem is that that call then recurses back to the convertFromDataSource() method.
I may be able to work around this, but it looks like a bona fide 1.0.0.alpha1 bug.
You probably were not focusing on this area for alpha2, but just for the record, the endless recursion still occurs in alpha2.
To summarize the problem (the following line numbers are from the alpha2 baseline):
I am calling
form.setitemDataSource() on a form which has previously been set with a FieldFactory.
This fieldFactory creates some TextFields and a
NativeSelect .
For each field that is created via the fieldFactory,
form.addField() (Form.java:770) is called.
In the case that is causing the recursion, I am putting a
NativeSelect on the form.
Form.addField() then calls (Form.java:530)
form.registerField() (Form.java:549).
The registerField() (AbstractField.java:443) method then calls the
nativeSelect.setReadThrough() method () (AbstractField.java:436) to set the readThrough mode of the component (NavtiveSelect) to match that of the form (Form.java:564).
In my case, the form must display the values from the backing datasource, so I am leaving the form setting of readThrough to its default value of true. Setting the readThrough state is not as trivial as you might think, because the value of the component must be synchronized with the datasource. Consequently…
The setReadthrough() method (AbstractField.java:443) calls
nativeSelect.convertFromDataSource() (AbstractField.java:798) to fetch the value from the datasource.
[b]
[/b]The call to
nativeSelect.convertFromDataSource() (AbstractField.java:798) is the beginning of the recursion.
<<<
I added a converter to my application’s converter factory for ObjectToNativeSelectConverter. (Without this converter I had other recursive errors.)
Because the NativeSelect has a converter (ObjectToNativeSelectConverter) it (AbstractField.java:801) calls the
converter.convertToPresentation() method in my code (ObjectToNativeSelectConverter.java).
Following the examples for other converters, my code calls
nativeSelect.getValue() (
AbstractSelect .java:620) to get the value of the NativeSelect.
This, in turn, calls
super .getValue() (
AbstractField .java:532) to get the selected value for the NativeSelect.
Finally the NativeSelect (AbstractField.java:533) calls
getFieldValue() (AbstractField.java:343) which decides whether to get the internal value of the field or the value from the datasource. In my case, since this is a readthrough component (see above), and the value is unmodified, it chooses to call
nativeSelect.convertFromDataSource() again.
[b]
[/b]This is a recursive call (see Step 5.) and the stack will explode at this point.
<<<
I went through this exercise partly so that I could understand in detail what was going on. But if there is an easy fix that can be applied to make this work, please let me know!
Step 7, calling getValue inside the converter, was something that caught my eye. I haven’t yet used converters so this might be the way to do it, but it still sounds odd. I noticed also that coverter seems to be of type NativeSelect ↔ Object. Usually you would want it to be between datatypes, like String ↔ Object. Are you sure that you should mention NativeSelect in that converter?
I had a weird issue with the nativeselect/combobox (any dropdown). Not sure if this only happens with sql server and especially when data is migrated from another legacy datasource.
I have multiple dropdowns (and other fields of course) in my form, for which i created corresponding lookup tables. Then i used FieldGroup to build and bind. Tried to save a record. JPA persist fails as nulls are passed in place of dropdown values.
This is what to do , just in case if any one is facing this issue:
Added a converter (passed a separate nativeselect field with all the properties set to the converter and just returned it in the convertToModel(Object value, Locale)) for each dropdown.
Then in the form/ui, get the same NativeSelect field and just bind to the fieldgroup and pass it to layout. generally we can use buildAndBind to the fieldgroup.
now the correct values are saved with out any issues.
But i still need to test this with edit screen, validations, value changes.