Unable to add both a Converter and a Validator to a TextField

I’m scratching my head trying to both add a Converter and set a Validator on a TextField that is meant for holding numbers and, again, I feel the process to accomplish it its very cumbersome and I’m experiencing a lot of problems.

Oh, and I want them to force to put two decimal zeros event if they want an integer amount.

Also, it must both accept and display the amounts in localized format; that is, a comma (,) for decimals and a dot (.) for thousands separataror.

For the 1st requirement, I tried to set up a Converter, which actually, after some research and, mainly, following the recommendations from
here
for StringToIntegerConverter (that say “A converter that converts from String to Integer and back. Uses the given locale and a NumberFormat instance for formatting and parsing.Override and overwrite getFormat(Locale) to use a different format”) , looks like this:

[code]
public class MyConverter extends StringToFloatConverter{

@Override
protected NumberFormat getFormat(Locale locale){
    if(locale == null){
        locale = Locale.getDefault();
    }
    return NumberFormat.getNumberInstance(locale);
}    

protected Float convertToNumber(String value, Class<? extends Number> targetType, Locale locale) throws ConversionException {
    if ((value == null)||(value.isEmpty())) {
        return null;
    }

    // Remove leading and trailing white space
    value = value.trim();

    NumberFormat formatter = null;
    try {
        formatter = NumberFormat.getNumberInstance(locale);
    }
    catch(Exception e){
        throw new ConversionException(e.getMessage());
    }
    
    Float parsedValue = null;
    try {
        parsedValue = formater.parse(value).floatValue();
    } 
    catch (ParseException ex) {
        throw new ConversionException(ex.getMessage());
    }
    
    return parsedValue;
}

@Override
public String convertToPresentation(Float value, Class<? extends String> targetType, Locale locale) throws ConversionException {
    if (value == null) {
        return null;
    }
    return getFormat(locale).format(value);
}

}
[/code]Then, for my 2nd requirement, I tried with a straightforward RegexpValidator that would take decimals up to the 2nd one, and it looks for my localized thousands (dot) and decimals (comma) separator:

new RegexpValidator("^(?:0|[1-9] [0-9] {0,2}(?:\\.[0-9] {3})*),[0-9] {2}", "Invalid amount" Well, in short, applying both of these to a TextField will fail miserably, no matter which one I apply before and / or if I change the format of the regexp to a non-localized one (by basically exchange the “.” by the “,” and viceversa). I’ve gone through a loooooot of debugging and have encountered some “strange” situations in the Vaadin part. For instance:

a) In AbstractValidator.java, isValidType(Object value) always returns false, because it always compares that a String.isAssignableFrom(Float), thus my explicit validate() always fails.

Moreover,

b) In MyConverter, method convertToNumber(String value, Class<? extends Number> targetType, Locale locale), in different calls (the converter is set in a couple of inputs, one of them for displaying a readonly value) the locale input variable has different values, sometimes being null, sometimes being the proper one. How is this?? A proper locale is set both in my machine and at the beggining of my Vaddin app. I haven’t been able to determine why, but in other several places in the code I’ve seen this same behaviour (alternating proper locale value or null). So when loading the same form, when the converter is called with a null locale, the converted value is totally nonsense, and when it’s called with the current locale, the displayed value inside the textfield is correct. Why? If I hardcode the locale value inside convertToNumber(), it always works in the proper way.

I need some suggestion in order to let both my validator and my converter cohabitate, or a workaround for solving this. And also some explanation on to what looks to me like a bug.

Hi,
Vaadin 7 or 8?

Sorry, I swear I was almost sure I posted it but it seems I deleted the line by mistake.

Vaadin 7.

Damn, I feared you say V7, because in V8 you can first add validator for string and then convert to number, but I have to think this a bit how to do it in 7.

Thanks Johannes. My current “workaround” is to totally avoid setting a converter for the value I want to change in my database; I get the validated String provided by the user, then convert it my own. A shame.

I also did some modifications to my original question, as the regular expression validator is for accepting any amount, up to two decimals, in my localized format (dots for thousands, commas for decimals).

You could also try to do the validation inside your custom converter. After checking form null and empty, but before parsing number do the regex validation. Then throw ConversionException with some descriptive error message.

EDIT: I forgot to add, that it’s not possible in V7 to validate uncoverted value. The validator is for the converted value, which is a shame and luckily fixed in V8 where you can have validator before and after conversion.

Another option would be to use the NumberField addon. It is available for Vaadin 7 and 8.

Instead of using a validator, could you implement a workaround like this?

if (!yourTextfield.getValue().matches(yourValidatorString)) { yourTextfield.setValue(""); } Put it in a value change listener and the textfield will “eat” any invalid data. You don’t get the nice red outline and tooltip, but it ensures the inputs are valid. You should then be able to convert the valid input to any form you want.

I’m not sure this is optimal, but I use converters that take the whole textfield:

resultingFloat = converter.boxToFloat(yourTextfield);

You can make a converter that includes any null checkers, roundings or data edge conditions you want.

I’ve had to move to this in Vaadin 8 because I don’t user binder (can’t overlay data binding to my existing data model) but still need a form of validation…

Thank you all for your suggestions. I think that for the moment I’ll stick to my current “solution” (not really a solution, though), as none is still 100% optimal, but probably will switch to one or a combination of yours, as my current one is probably the worst one… Thanks again for inspiring :wink: