Making sense of all the events fired on a TextField

I am trying to figure out the best place to put my code, and have written a small test program to check the order in which events are fired.

The program has a converter, a validator and valuechanged on field, and valuechanged on property:

public class TestUI extends UI {

    static Logger log = Logger.getLogger(TestUI.class.getName());

    static class MyTextField extends TextField {
        
        public MyTextField(Property dataSource) {
            super(dataSource);
        }

        @Override
        public void commit() throws SourceException, InvalidValueException {
            log.fine("field.commit");
            super.commit();
        }
    }
    
    @Override
    protected void init(VaadinRequest paramVaadinRequest) {

        VerticalLayout verticalLayout = new VerticalLayout();
        setContent(verticalLayout);
        
        final ObjectProperty<String> property = new ObjectProperty<String>("Hello");

        /* Test event ordering */
        
        final TextField field = new MyTextField(property);
        field.setBuffered(true);
        field.setImmediate(true);
        verticalLayout.addComponent(field);
        
        field.addValidator(new Validator() {
            
            @Override
            public void validate(Object value) throws InvalidValueException {
                log.fine("field.validate: <" + value + ">, <" + property.getValue() + ">");
                
                if("x".equals(value)) {
                    throw new InvalidValueException("Don't like it");
                }
            }
        
        });
        
        field.addValueChangeListener(new ValueChangeListener() {
            
            @Override
            public void valueChange(ValueChangeEvent event) {
                log.fine("field.valueChange: <" + event.getProperty().getValue() + ">, <" + property.getValue() + ">");
            }
        
        });
        
        field.setConverter(new Converter<String, String>() {

            @Override
            public String convertToModel(String value, Class<? extends String> targetType, Locale locale) throws com.vaadin.data.util.converter.Converter.ConversionException {
                log.fine("field.convertToModel: <" + value + ">, <" + property.getValue() + ">");
                return value;
            }

            @Override
            public String convertToPresentation(String value, Class<? extends String> targetType, Locale locale) throws com.vaadin.data.util.converter.Converter.ConversionException {
                log.fine("field.convertToPresentation: <" + value + ">, <" + property.getValue() + ">");
                return value;
            }

            @Override
            public Class<String> getModelType() {
                return String.class;
            }

            @Override
            public Class<String> getPresentationType() {
                return String.class;
            }
        });
        
        property.addValueChangeListener(new ValueChangeListener() {
            
            @Override
            public void valueChange(ValueChangeEvent event) {
                log.fine("property.valueChange: <" + event.getProperty().getValue() + ">, <" + property.getValue() + ">");
            }
        });
        
        Button button = new Button("Commit", new ClickListener() {
            
            @Override
            public void buttonClick(ClickEvent event) {
                field.commit();
            }
        });
        
        verticalLayout.addComponent(button);
        
    }


First for a buffered field, which I almost understand:

When the test-screen is opened I get:

field.convertToModel: <Hello>, <Hello>
field.validate: <Hello>, <Hello>

Here I had expected a “convertToPresentation”…!?
Personally I would also prefer it if validate was only called on user-changes.

Then I enter “World” into the field and tab out:

field.convertToModel: <World>, <Hello>
field.convertToPresentation: <World>, <Hello>
field.valueChange: <World>, <Hello>
field.convertToModel: <World>, <Hello>
field.validate: <World>, <Hello>

Immediately I didn’t see the need for (2), but I guess it means that when the user enters “2/3” this can be converted to 0.666666… and then be displayed back as 0.67, or somesuch.

I don’t see the point of (4) however.

Then I click on the Commit button and get:

field.commit
field.convertToModel: <World>, <Hello>
field.validate: <World>, <Hello>
field.convertToModel: <World>, <Hello>
property.valueChange: <World>, <World>
field.convertToModel: <World>, <World>
field.validate: <World>, <World>

Here I’m lost. Since the field has already been validated, why does it need to be validated again? Twice?
I would have expected only (1) and (5)…


Anyway, now to the fun stuff. Change the field to non bufffered and try again.

When the test-screen is opened I get:

field.convertToPresentation: <Hello>, <Hello>
field.convertToModel: <Hello>, <Hello>
field.validate: <Hello>, <Hello>
field.convertToPresentation: <Hello>, <Hello>

So, I have gained (1) and (4).
I don’t see why this should be different for a non-buffered field.

Then I enter “World” into the field and tab out:

field.convertToPresentation: <Hello>, <Hello>
field.convertToModel: <World>, <Hello>
field.convertToPresentation: <World>, <Hello>
field.convertToModel: <World>, <Hello>
field.validate: <World>, <Hello>
field.convertToModel: <World>, <Hello>
property.valueChange: <World>, <World>
field.convertToPresentation: <World>, <World>
field.valueChange: <World>, <World>
field.convertToPresentation: <World>, <World>
field.convertToModel: <World>, <World>
field.validate: <World>, <World>
field.convertToPresentation: <World>, <World>

5 convertToPresentation, 4 convertToModel, 2 validate, 1 property.valueChange and 1 field.valueChange…
This seems a tad extreme?