Bug with Table and PropertyFormatter?

Hi;

I think I may have found a bug with how the table handles PropertyFormatters in fields embedded in the table.

First a little bit of background:

In my application; admins are able to mass-upload user data from an excel sheet. After the excel sheet is uploaded and parsed; I stuff the results in the form of UserInfo beans in a container. A table sits on top of this container and displays the results of the upload-parse process; at which point admins can correct possible errors in the excel sheet prior to persisting the new users to the backend via Hibernate.

Because the excel sheet can contain errors; I need to display validators in the table to flag missing required fields; invalid email addresses and so forth.

Now, by default; Vaadin tables are in readOnly mode and so the data being rendered isn’t being validated in any way, shape or form. To get around that, my table has a TableFieldFactory which tracks the current row and property field that an admin is working on. If they match; the TableFieldFactory returns an editable field. If they don’t; then it returns a TextField which is .setReadOnly(true). This allows me to still render the table in “readOnly” mode; but because each cell is now an actual textField; I can add validators and so display errors for all rows; even the one’s that an admin isn’t currently adding.

All this works fine.

However; some of my “static TextFields” are actually backed by nested beans on the User object (specifically; users can’t be assigned to Accounts; which are themselves complex objects).

In editable form; the account property is rendered as a ComboBox; so the admin can select a different account from what was originally entered in the excel sheet.In readOnly form however; I can’t just return the ComboBox with setReadOnly(true) - I found when I did that; first of all the comboBox looks weird; second of all; it kills performance on the table.

So instead; in “readOnly” mode; I return a TextField with setReadOnly(true) for the nested beans; and the TextField gets given a PropertyFormatter so that it’s able to render the nested bean as a meaningfull text representation. Like so:


if(propertyId.equals(UserField.ACCOUNT.getPropertyName()))
			{
				TextField accountField= new TextField(UserField.ACCOUNT.getCaption());
				accountField.setPropertyDataSource(new PropertyFormatter(container.getItem(itemId).getItemProperty(propertyId))
				{

					@Override
					public String format(Object value) {
						return ((Account)value).getAccountCode();
					}

					@Override
					public Object parse(String formattedValue) throws Exception {
						// TODO Auto-generated method stub
						return null;
					}
					
					
				});
				accountField.setWidth(FIELD_WIDTH);
				accountField.setRequired(true);
				accountField.setReadOnly(true);
				return accountField;
				
			}

(parse returns null because this will never get called; the field is in readOnly mode; if the user clicks it; a different field will get rendered and the user will get the full fledgded combobox instead).

This should work in principle; however; I’m finding that while the PropertyFormatter.format() method IS getting called; the Table is still outputting the .toString() method on the underlying nested Bean (in this case; the Account object of the user).

After looking at the table code; I discovered why:

For each cell in the table; the Table component calls this method:


protected Object getPropertyValue(Object rowId, Object colId,
            Property property) {
        if (isEditable() && fieldFactory != null) {
            final Field f = fieldFactory.createField(getContainerDataSource(),
                    rowId, colId, this);
            if (f != null) {
                f.setPropertyDataSource(property);
                return f;
            }
        }

        return formatPropertyValue(rowId, colId, property);
    }

So you can see it calls my fieldFactory to create the TextField; with the attached PropertyFormatter as a propertyDataSource. The problem is that right AFTER that; it sets the propertyDataSource to just the basic property; which basically undoes all my work with the PropertyFormatter: the field is now, once again; backed directly by the nestedBean; rather than the PropertyFormatter.

I believe, instead, the code for this should be:


protected Object getPropertyValue(Object rowId, Object colId,
            Property property) {
        if (isEditable() && fieldFactory != null) {
            final Field f = fieldFactory.createField(getContainerDataSource(),
                    rowId, colId, this);
            [b]
if (f != null)
            {
                if(f.getPropertyDataSource == null) 
                {
                       f.setPropertyDataSource(property); 
                }

            return f;
            }
[/b]
        }

        return formatPropertyValue(rowId, colId, property);
    }

This fixes the problem - if the field already has a PropertyDataSource; that propertyDataSource is used; if it isn’t; then it’s backed by the simple object itself.

And I see you’ve also come up with a fix already. That’s great :), but the forums are not the right place to report bugs. Please
submit a bug report
to the Vaadin bug tracker instead. Then you can be sure that our R&D team will have a look at it.