(should anyone need them) ANumberField and APopupDatetimeField

As I lost lots of hours (re)implementing these in Vaadin 8.x, I thought posting the code could be useful for someone else.

ANumberField: stores a BigDecimal in a TextField, and uses get/setValue(String) or get/setConvertedValue(BigDecimal).

public class ANumberField extends TextField {

    protected MNumberConverter numberConverter = null;

    protected BigDecimal convertedValue = null;

    /* */

    public ANumberField(MNumberConverter numberConverter) {
        super();
        //
        this.setNumberConverter(numberConverter);
        //
        this.addStyleName("mzNumberField");
    }

    public ANumberField(MNumberConverter numberConverter, String caption) {
        this(numberConverter);
        //
        this.setCaption(caption);
    }

    public ANumberField(MNumberConverter numberConverter, String caption, BigDecimal convertedValue) {
        this(numberConverter, caption);
        //
        this.setConvertedValue(convertedValue);
    }

    /* Number converter. */

    public void setNumberConverter(MNumberConverter numberConverter) {
        if (null == numberConverter) {
            throw new IllegalArgumentException("Invalid 'numberConverter': null.");
        }
        //
        this.numberConverter = numberConverter;
    }

    public MNumberConverter getNumberConverter() {
        return this.numberConverter;
    }

    /* Conversion. */

    protected void doSetValue(String value) {
        if ((MText.isBlank(value)) || (this.getEmptyValue().equals(value))) {
            this.getState().text = this.getEmptyValue();
            this.convertedValue = null;
        }
        else {
            try {
                this.getState().text = this.getNumberConverter().getStringFromNumber(this.getNumberConverter().getNumberFromString(value));
                this.convertedValue = this.getNumberConverter().getNumberFromString(this.getState().text);
            }
            catch (MInvalidConversionFormatException exception) {
                this.getState().text = this.getEmptyValue();
                this.convertedValue = null;
            }
        }
    }

    public void setConvertedValue(BigDecimal convertedValue) {
        if (null == convertedValue) {
            this.getState().text = this.getEmptyValue();
            this.convertedValue = null;
        }
        else {
            try {
                this.setValue(this.getNumberConverter().getStringFromNumber(convertedValue));
                this.convertedValue = this.getNumberConverter().getNumberFromString(this.getValue());
            }
            catch (MInvalidConversionFormatException exception) {
                this.setValue(this.getEmptyValue());
                this.convertedValue = null;
            }
        }
    }

    public BigDecimal getConvertedValue() {
        return this.convertedValue;
    }

}

APopupDatetimeField: works with LocalDateTime in different formats in a DateTimeField.

public class APopupDatetimeField extends DateTimeField {

    protected MLocalDateTimeConverter datetimeConverter = null;

    /* */

    public APopupDatetimeField(Locale locale, MLocalDateTimeConverter datetimeConverter) {
        super();
        //
        this.setLocale(locale);
        //
        this.setDatetimeConverter(datetimeConverter);
        //
        this.setDateFormat(datetimeConverter.getDefaultDatetimeFormat());
        this.setLenient(false);
        //
        this.addStyleName("mzPopupDatetimeField");
    }

    public APopupDatetimeField(Locale locale, MLocalDateTimeConverter datetimeConverter, String caption) {
        this(locale, datetimeConverter);
        //
        this.setCaption(caption);
    }

    public APopupDatetimeField(Locale locale, MLocalDateTimeConverter datetimeConverter, String caption, LocalDateTime value) {
        this(locale, datetimeConverter, caption);
        //
        this.setValue(value);
    }

    /* Datetime converter. */

    public void setDatetimeConverter(MLocalDateTimeConverter datetimeConverter) {
        if (null == datetimeConverter) {
            throw new IllegalArgumentException("Invalid 'datetimeConverter': null.");
        }
        //
        this.datetimeConverter = datetimeConverter;
        //
        String defaultDatetimeFormat = datetimeConverter.getDefaultDatetimeFormat();
        if (defaultDatetimeFormat.contains("s")) {
            this.setResolution(DateTimeResolution.SECOND);
        }
        else if (defaultDatetimeFormat.contains("m")) {
            this.setResolution(DateTimeResolution.MINUTE);
        }
        else if ((defaultDatetimeFormat.contains("H")) || (defaultDatetimeFormat.contains("k")) || (defaultDatetimeFormat.contains("K")) || (defaultDatetimeFormat.contains("h")) || (defaultDatetimeFormat.contains("a"))) {
            this.setResolution(DateTimeResolution.HOUR);
        }
        else if (defaultDatetimeFormat.contains("d")) {
            this.setResolution(DateTimeResolution.DAY);
        }
        else if (defaultDatetimeFormat.contains("M")) {
            this.setResolution(DateTimeResolution.MONTH);
        }
        else {
            this.setResolution(DateTimeResolution.YEAR);
        }
    }

    public MLocalDateTimeConverter getDatetimeConverter() {
        return this.datetimeConverter;
    }

    /* Conversion. */

    protected Result<LocalDateTime> handleUnparsableDateString(String datetimeString) {
        if (MText.isBlank(datetimeString)) {
            return Result.ok(null);
        }
        //
        try {
            return Result.ok(this.getDatetimeConverter().getDatetimeFromString(datetimeString));
        }
        catch (MInvalidConversionFormatException exception) {
            return Result.ok(null);
        }
    }

}

If you need details about the converters, you can browse the Macaco source code in
my WebSVN
.

Thanks,
Marco

First of all, thanks for your efforts.

Apparently, the your code does not look complete, and the forum is not the very best place to share it. Why don’t you make your own addon for Vaadin Directory? https://vaadin.com/directory#!browse

There is another feature in In upcoming Vaadin 8.1, field internal validators. Your inner validators should be exposed through that API to make Binder supporting all the validation stuff.

Ilia, thanks for your comment.
I will consider creating a specific add-on, as soon as I find some time :slight_smile:

Is there a specific tutorial for this? I found https://vaadin.com/directory/help/creating-vaadin-add-ons, is it still valid?

Thanks,
Marco

Yep, that should be up to date. If you use an add-on archetype (I like this one: https://github.com/viritin/archetype-vaadin-gwt-addon and this one: https://github.com/viritin/archetype-vaadin-addon), it will do most of the hard configuration work for you :slight_smile:

-Olli