Why does StringLengthValidator not enforce minimum length when length is 0?

If I create a
StringLengthValidator
with a minimum length of 3, the behavior I see when the user does data entry of a color word:
Red =
valid

Re =
invalid

R =
invalid

=
valid
(empty string, length is zero)

Is that last one a feature or a bug? Seems like a bug to me: ( 0 < 3 ) ∴ invalid

Here is a tiny little example app to demonstrate the issue.

package com.example.completedlayout;

import com.vaadin.Application;
import com.vaadin.data.Validator;
import com.vaadin.ui.*;

public class CompletedlayoutApplication extends Application {

    TextField favoriteColorField = null;
    TextField someOtherField = null;

    @Override
    public void init() {
        Window mainWindow = new Window( "Completedlayout Application" );
        mainWindow.setImmediate( true );

        VerticalLayout layout = new VerticalLayout();

        // Favorite color field
        this.favoriteColorField = new TextField( "Favorite color:" );
        this.favoriteColorField.setImmediate( true );
        // StringLengthValidator(String errorMessage, int minLength, int maxLength, boolean allowNull)
        [color=#02b90e]
Validator stringLengthValidator = new com.vaadin.data.validator.StringLengthValidator( "Required field. Type 3-10 characters.", [color=#db0312]
3
[/color], [color=#db0312]
10
[/color], false );
[/color]
        this.favoriteColorField.addValidator( stringLengthValidator );
        layout.addComponent( this.favoriteColorField );

        // Some other field - irrelevant.
        this.someOtherField = new TextField( "Some other field:" );
        this.someOtherField.setImmediate( true );
        layout.addComponent( this.someOtherField );

        mainWindow.setContent( layout );
        setMainWindow( mainWindow );
    }

}

StringLengthValidator does have a setting about whether to consider Null values as valid or not. But my case above is not about Null. It is about an empty String where length is zero.

I looked at the source code for StringLengthValidator, to see if I could find a bug. This key piece from the “isValid” method seems correct. So it seems some other code in some other place is overriding the results of this validity test.

        if ([color=#01b825]
(minLength >= 0 && len < minLength)
[/color]
                || (maxLength >= 0 && len > maxLength)) {
            return false;
        }

Well, I just found this
ticket 3124
saying this behavior is a feature.

I created this
dev ticket
asking to change this behavior.

Can someone suggest how I might be able to override this behavior?

In what class is the code that decides to ignore all validators because the value’s String length is zero?

I really don’t want to have to make hundreds of fields “mandatory” and therefore display all of them with a red asterisk that never goes away.

–Basil Bourque

I have a workaround. I wrote a subclass of TextField to overload AbstractTextField’s overload of the “isEmpty” method. I restore the definition of “isEmpty” to that used by AbstractTextField’s superclasses

protected boolean isEmpty() {
    return ( getValue() == null );
}

Here is the source code for my subclass. You may want to make similar subclasses of “TextArea” and “PasswordField”.

So many lines of code just to deliver the single "return’ line seen above!

/**
 * 
 */
package com.example.completedlayout;

import com.vaadin.data.Property;
import com.vaadin.ui.TextField;

/**
 * Subclass of "TextField" in Vaadin 6.
 * 
 * Overloads the "isEmpty" method. That is the only important feature of this class.
 * Constructors are here only for compatibility, with no added value.
 * 
 * One simple purpose: Overrule the unfortunate behavior of TextField to ignore validators 
 * if the value's String length is zero unless the field is "Required".
 * 
 * The "AbstractTextField" class overloads the "isEmpty" method to mean:
 * "is null OR is String value of zero length?". 
 * The superclasses above AbstractTextField define "isEmpty" method to mean only "is null?". 
 * 
 * This different behavior of all AbstractTextField's subclasses (TextField, TextArea, and 
 * PasswordField) all suffer from a drastic effect:
 * --> All assigned Validators are ignored when the String value is empty. 
 * The workaround to this feature is to make the field "Required".
 * 
 * For example, suppose a TextField is assigned a StringLengthValidator defined with a minimum length of 3.
 * The behavior seen when the user does data entry of the word 'Red':
 * Red = valid
 * Re = invalid
 * R = invalid
 * = valid (empty string, length is zero) --> BAD. All assigned Validators ignored.
 * 
 * To restore the Validators, you must call the TextField method "isRequired(true)" 
 * which adds behavior you may not want such as adding an always-present red asterisk on the Layout.
 * Also this is redundant, as assigning a "StringLengthValidator" with a minimum 
 * length of 0 has the effect of making the field required.
 * 
 * If you use this subclass of TextField, you will see this behavior for an assigned "StringLengthValidator"
 * where minimum length is 3:
 * Red = valid
 * Re = invalid
 * R = invalid
 * = invalid (empty string, length is zero)
 * 
 * See my posting on the Vaadin forum: 
 * https://vaadin.com/forum/-/message_boards/view_message/1418846#_19_message_1418818
 * 
 * See this bug report where TextField's special behavior is explained as a feature:
 * http://dev.vaadin.com/ticket/3124
 * 
 * See my dev ticket to alter TextField's behavior to match all the other similar fields:
 * http://dev.vaadin.com/ticket/8818
 * 
 * CAUTION:
 * Use this class at your own risk. I have no idea if it is safe. I have no idea of the possible
 * side-effects. I have perused a few of the Vaadin source files. I have not yet noticed any
 * bad consequences, but I cannot be sure.
 * 
 * © 2012 Basil Bourque. 
 * This source code may be used freely forever by anyone taking full responsibility for doing so.
 * 
 * @author Basil Bourque
 * 
 */
public class TextField_IsEmptyMeansIsNull extends TextField {

    /**
     * Constructor. 
     * No added behavior; here just for compatibility with superclass.
     */
    public TextField_IsEmptyMeansIsNull() {
        super();
    }

    /**
     * Constructor. 
     * No added behavior; here just for compatibility with superclass.
     * 
     * @param dataSource
     */
    public TextField_IsEmptyMeansIsNull( Property dataSource ) {
        super( dataSource );
    }

    /**
     * Constructor. 
     * No added behavior; here just for compatibility with superclass.
     * 
     * @param caption
     * @param dataSource
     */
    public TextField_IsEmptyMeansIsNull( String caption, Property dataSource ) {
        super( caption, dataSource );
    }

    /**
     * Constructor. 
     * No added behavior; here just for compatibility with superclass.
     * 
     * @param caption
     * @param value
     */
    public TextField_IsEmptyMeansIsNull( String caption, String value ) {
        super( caption, value );
    }

    /**
     * Constructor. 
     * No added behavior; here just for compatibility with superclass.
     * 
     * @param caption
     */
    public TextField_IsEmptyMeansIsNull( String caption ) {
        super( caption );
    }

    /**
     * Is the field empty?
     * 
     * In general, "empty" state is same as null. As an exception, TextField
     * also treats empty string as "empty".
     * 
     * This class overloads TextField's overload of "isEmpty" method, to restore the behavior seen 
     * in the AbstractTextField's superclasses where "isEmpty" means "is null?".
     */
    protected boolean isEmpty() {
        return ( getValue() == null );
    }

}

Yaaay thanks for suggestion!

+1