Converters

I am building a form with a NativeSelect on it.

I am running into converter exceptions.

Since this is a single-select component, it wants to convert to Object. But my IDs are typically Strings.

It looks like I will have to make converters for a potentially exploding number of permutations.

Am I understanding this correctly or is there a simpler way?

Specifically, I am getting this message with the red dot next to my NatveSelect on my Form:

Could not convert value to Object

Unfortunately, that’s the entire message. There’s no stack trace. So I’m having a hard time figuring out what to do with that.

In vaadin’s AbstractField.java (7.0.0.alpha1) at line 1051 are these lines:

        try {
            valueToValidate = getConverter().convertToModel(fieldValue,
                    getLocale());
        } catch (Exception e) {
            throw new InvalidValueException(
                    getConversionError(getConverter().getModelType()));
        }

It looks like the original Exception e (in my case a CastClassException) is not being passed along to the new InvalidValueException.

And subsequently, when the InvalidValueException is caught at line 1006, no message is printed:

    } catch (InvalidValueException e) {
        return false;
    }

Including a stack trace somewhere in this error chain would be helpful.

So, what is the source of my CastClassException?

It’s complicated…

In this line of code, which is called in the validate() method of AbstractField:

valueToValidate = getConverter().convertToModel(fieldValue,getLocale());

The getConverter() method is returning a ReverseConverter. ReverseConverted is a generic class defined like this:

class ReverseConverter<PRESENTATION, MODEL>

ReverseConverter has a field named realConverter which is defined like this:

private Converter<MODEL, PRESENTATION> realConverter;

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:

  1. the type of the realConverter is ObjectToNativeSelectConverter, which is a class I wrote and set in my converter factory.
  2. 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).

Still digging … stay tuned …

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?

Here is some more background on my architecture:

  1. I have a table backed by a container
  2. The sub-window/form is brought up when the user want to edit one row from the table
  3. The form is defined by the same item which backs the row in the table
  4. First I call
    form.setItemDataSource(tableItem)
    to set the backing item on the form
  5. 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.

Can anyone shed any light on what is going on?

Digging into this has led me to discover the following recursion (in red):

Form.bindPropertyToField(Object, Property, Field) line: 801
NativeSelect(AbstractField<T>).setPropertyDataSource(Property) line: 758
NativeSelect(AbstractField<T>).[color=#ff0000]
convertFromDataSource(Object) line: 836
[/color]
[b]
ObjectToNativeSelectConverter.convertToPresentation(Object, Locale) line: 1
ObjectToNativeSelectConverter.convertToPresentation(NativeSelect, Locale) line: 25

[/b]NativeSelect(AbstractSelect).getValue() line: 584
NativeSelect(AbstractField<T>).getValue() line: 565
NativeSelect(AbstractField<T>).getFieldValue() line: 383
NativeSelect(AbstractField<T>).[color=#ff0000]
convertFromDataSource(Object) line: 836
[/color]

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):

  1. I am calling
    form.setitemDataSource()
    on a form which has previously been set with a FieldFactory.

This fieldFactory creates some TextFields and a
NativeSelect
.

  1. 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.

  1. Form.addField() then calls (Form.java:530)
    form.registerField()
    (Form.java:549).

  2. 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…

  1. 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.)

  1. Because the NativeSelect has a converter (ObjectToNativeSelectConverter) it (AbstractField.java:801) calls the
    converter.convertToPresentation()
    method in my code (ObjectToNativeSelectConverter.java).

  2. Following the examples for other converters, my code calls
    nativeSelect.getValue()
    (
    AbstractSelect
    .java:620) to get the value of the NativeSelect.

  3. This, in turn, calls
    super
    .getValue() (
    AbstractField
    .java:532) to get the selected value for the NativeSelect.

  4. 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!

:slight_smile:

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?

Which examples are you referring to?

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.