Rebinding fields in a FieldGroup -- exception concurrent modification

I have a FieldGroup that I setup – before any Item is set – along with the Layout of those fields. However, one of those fields varies depending on the actual Item set (via setItemDataSource). I’m looking for how to replace a given field in a fieldgroup/layout to another based on the item’s type (based on a property value the item holds).

That is, if Item is “type 1,” then the Field in the FieldGroup/Layout is a TextField.

If that Item is “type 2”, then the same Field in the FieldGroup/Layout is a NativeSelect. And in our case, other types of the Item will use different select boxes to control the content of the field.

When I initially create the layout/fieldgroup, I am just using a TextField placeholder. Then on setItemDataSource, I want to unbind the field and rebind using the new field, and then replace the old component in my layout with the new field component.

This works with a standard master-view relationship in which the user selects which record from a Table, and that Table then triggers a value change on the Table, and we use the selected row’s Item to then set the Item for the form.

But I get this exception:

Caused by: java.util.ConcurrentModificationException
        at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:711)
        at java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:734)
        at com.vaadin.data.fieldgroup.FieldGroup.setItemDataSource(FieldGroup.java:104)
        at com.esignforms.open.vaadin.config.reportTemplate.reportField.ReportTemplateReportFieldForm.setItemDataSource(ReportTemplateReportFieldForm.java:464)
        at com.esignforms.open.vaadin.config.reportTemplate.reportField.ReportTemplateReportFieldView.valueChange(ReportTemplateReportFieldView.java:107)

When handling the new Item, I am basically trying stuff like:

        // Determine which fields have special output format specs. If the field is a File type, there is no output spec at all.
        Field<?> outputFormatSpec = (Field<?>)fieldGroup.getField("outputFormatSpec");
        if ( outputFormatSpec != null ) {
            outputFormatSpec.discard();
            vaadinUi.removeAllValidators(outputFormatSpec);
            fieldGroup.unbind(outputFormatSpec);
        }

followed by ....

            fieldLayout.replaceComponent(outputFormatSpec, select); // select is a NativeSelect I'm replacing the orig field with
            fieldGroup.bind(select,"outputFormatSpec");

A quick add-on, this was working fine using the deprecated Form subclassing, but I’m porting this over to use FieldGroups and its own layout instead.

When subclassing from Form, this worked using this sort of code:

        Field outputFormatSpec = (Field)getField("outputFormatSpec");

        if ( outputFormatSpec != null ) {
            outputFormatSpec.discard();
            vaadinUi.removeAllValidators(outputFormatSpec);
            detachField(outputFormatSpec);
        }

followed later by....

            select.setPropertyDataSource(getItemDataSource().getItemProperty("outputFormatSpec"));
            addField("outputFormatSpec",select);

RESOLVED, but odd didn’t need this before when using the deprecated Form subclassing, but has the issue using FieldGroup.

It turns out that one of the fields (a select box) has a value change listener that updates some of the UI based on the selected value. When doing a fieldGroup.setItemDataSource, this causes the value to change and that select’s ValueChangeListener to trigger, which then tries to update the field group. This caused the concurrent modification exception.

To get around this, I have a variable that I set just before I call fieldGroup.setItemDataSource and unset after it returns so the select’s ValueChangeListener can test that variable and skip doing the related fieldgroup changes.

So my handler for new items is:

    public void setItemDataSource(Item newDataSource) { // called when reloading a form so we can build a type-appropriate version
        doingNewItemDataSource = true;
        if ( newDataSource != null ) {
            fieldGroup.setItemDataSource(newDataSource);
            setupAllFieldNames((EsfUUID)newDataSource.getItemProperty("reportFieldTemplateId").getValue());
            fieldLayout.setVisible(true);
        } else {
            fieldGroup.setItemDataSource(null);
            fieldLayout.setVisible(false);
        }
        setReadOnly(isReadOnly());
        doingNewItemDataSource = false;
    }

and then my select’s value change is:

        reportFieldTemplateId.addValueChangeListener( new ValueChangeListener() {
            @Override
            public void valueChange(com.vaadin.data.Property.ValueChangeEvent event) {
                EsfUUID newReportFieldTemplateId = (EsfUUID)event.getProperty().getValue();
                if ( newReportFieldTemplateId != null && ! newReportFieldTemplateId.isNull() ) {
                    if ( ! doingNewItemDataSource )
                        setupAllFieldNames(newReportFieldTemplateId);
                }
            }
        });