com.haulmont.cuba.desktop.gui.components.DesktopAbstractTextField.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.desktop.gui.components.DesktopAbstractTextField.java

Source

/*
 * Copyright (c) 2008-2016 Haulmont.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.haulmont.cuba.desktop.gui.components;

import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.chile.core.model.MetaPropertyPath;
import com.haulmont.chile.core.model.Range;
import com.haulmont.chile.core.model.utils.InstanceUtils;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.core.global.UserSessionSource;
import com.haulmont.cuba.desktop.gui.executors.impl.DesktopBackgroundWorker;
import com.haulmont.cuba.desktop.sys.DesktopToolTipManager;
import com.haulmont.cuba.desktop.sys.validation.ValidationAlertHolder;
import com.haulmont.cuba.gui.AppConfig;
import com.haulmont.cuba.gui.components.Frame;
import com.haulmont.cuba.gui.components.TextInputField;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.data.impl.WeakItemChangeListener;
import com.haulmont.cuba.gui.data.impl.WeakItemPropertyChangeListener;
import org.apache.commons.lang.StringUtils;

import javax.swing.*;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.text.ParseException;
import java.util.Locale;
import java.util.Objects;

public abstract class DesktopAbstractTextField<T extends JTextComponent> extends DesktopAbstractField<T> {

    protected Document doc;

    protected Datatype datatype;

    protected boolean updatingInstance;
    protected Object prevValue;

    protected Datasource datasource;

    protected int maxLength;
    protected boolean trimming = true;

    protected Locale locale = AppBeans.get(UserSessionSource.class).getLocale();
    protected DefaultValueFormatter valueFormatter;

    protected Datasource.ItemChangeListener itemChangeListener;
    protected Datasource.ItemPropertyChangeListener itemPropertyChangeListener;

    protected DesktopAbstractTextField() {
        doc = createDocument();

        impl = createTextComponentImpl();

        impl.setEnabled(enabled);
        impl.setEditable(editable);
        impl.setDocument(doc);
        impl.setVisible(isVisible());

        TextFieldListener listener = createTextListener();
        impl.addKeyListener(listener);
        impl.addFocusListener(listener);
        impl.putClientProperty(getSwingPropertyId(), getId());

        updateMissingValueState();

        valueFormatter = new DefaultValueFormatter(locale);
    }

    protected Document createDocument() {
        return new TextComponentDocument();
    }

    protected TextFieldListener createTextListener() {
        return new TextFieldListener();
    }

    protected abstract T createTextComponentImpl();

    @Override
    public void updateMissingValueState() {
        boolean state = isRequired() && isEditableWithParent() && StringUtils.isBlank(impl.getText());
        decorateMissingValue(impl, state);
        if (getComposition() instanceof JScrollPane) {
            decorateMissingValue(getComposition(), state);
        }
    }

    @Override
    protected void setEditableToComponent(boolean editable) {
        if (impl != null) {
            impl.setEditable(editable);
            updateMissingValueState();
        }
    }

    @Override
    public void updateEnabled() {
        super.updateEnabled();

        if (impl != null) {
            impl.setEnabled(isEnabledWithParent());
        }
    }

    @Override
    protected void setCaptionToComponent(String caption) {
        super.setCaptionToComponent(caption);

        requestContainerUpdate();
    }

    @Override
    public String getDescription() {
        return impl.getToolTipText();
    }

    @Override
    public void setDescription(String description) {
        if (!Objects.equals(this.getDescription(), description)) {
            impl.setToolTipText(description);
            DesktopToolTipManager.getInstance().registerTooltip(impl);

            requestContainerUpdate();
        }
    }

    @Override
    public <V> V getValue() {
        String text = getImpl().getText();
        Object rawValue = validateRawValue(text);
        if (rawValue instanceof String) {
            rawValue = StringUtils.trimToNull((String) rawValue);
        }

        //noinspection unchecked
        return (V) rawValue;
    }

    @Override
    public void setValue(Object value) {
        DesktopBackgroundWorker.checkSwingUIAccess();

        if (!Objects.equals(prevValue, value)) {
            updateInstance(value);
            updateComponent(value);
            fireChangeListeners(value);
        } else {
            updateComponent(prevValue);
        }
    }

    @Override
    public Datasource getDatasource() {
        return datasource;
    }

    @Override
    public MetaProperty getMetaProperty() {
        return metaProperty;
    }

    @Override
    public MetaPropertyPath getMetaPropertyPath() {
        return metaPropertyPath;
    }

    @Override
    public void setDatasource(Datasource datasource, String property) {
        this.datasource = datasource;

        if (datasource == null) {
            setValue(null);
            return;
        }

        resolveMetaPropertyPath(datasource.getMetaClass(), property);
        valueFormatter.setMetaProperty(metaProperty);

        itemChangeListener = e -> {
            if (updatingInstance) {
                return;
            }

            Object value = InstanceUtils.getValueEx(e.getItem(), metaPropertyPath.getPath());
            updateComponent(value);
            fireChangeListeners(value);
        };
        //noinspection unchecked
        datasource.addItemChangeListener(new WeakItemChangeListener(datasource, itemChangeListener));

        itemPropertyChangeListener = e -> {
            if (updatingInstance) {
                return;
            }

            if (e.getProperty().equals(metaPropertyPath.toString())) {
                updateComponent(e.getValue());
                fireChangeListeners(e.getValue());
            }
        };
        //noinspection unchecked
        datasource.addItemPropertyChangeListener(
                new WeakItemPropertyChangeListener(datasource, itemPropertyChangeListener));

        initRequired(metaPropertyPath);

        if ((datasource.getState() == Datasource.State.VALID) && (datasource.getItem() != null)) {
            Object value = InstanceUtils.getValueEx(datasource.getItem(), metaPropertyPath.getPath());
            updateComponent(value);
            fireChangeListeners();
        }

        Integer maxLength = (Integer) metaProperty.getAnnotations().get("length");
        if (maxLength != null && this instanceof TextInputField.MaxLengthLimited) {
            ((TextInputField.MaxLengthLimited) this).setMaxLength(maxLength);
        }

        if (metaProperty.isReadOnly()) {
            setEditable(false);
        }

        initBeanValidator();
    }

    @Override
    protected boolean isEmpty(Object value) {
        if (value instanceof String)
            return StringUtils.isBlank((String) value);
        else
            return value == null;
    }

    protected void showValidationMessage() {
        if (ValidationAlertHolder.isListen()) {
            ValidationAlertHolder.validationFailed();
        }

        impl.requestFocus();

        Messages messages = AppBeans.get(Messages.NAME);
        DesktopComponentsHelper.getTopLevelFrame(this).showNotification(
                messages.getMessage(AppConfig.getMessagesPack(), "validationFail"), Frame.NotificationType.TRAY);
    }

    protected Object validateRawValue(String rawValue) {
        if (trimming && rawValue != null)
            rawValue = rawValue.trim();

        if ((datasource != null) && (metaPropertyPath != null)) {
            Range range = metaProperty.getRange();
            if (range.isDatatype())
                datatype = metaPropertyPath.getRange().asDatatype();
            if (range.isClass())
                return prevValue;
            if (range.isEnum()) {
                try {
                    return range.asEnumeration().parse(rawValue, locale);
                } catch (ParseException e) {
                    showValidationMessage();
                    return prevValue;
                }
            }
        }
        if (datatype != null) {
            try {
                // double conversion to verify type constraints
                // used for properly parsing BigDecimal values
                Object datatypeValue = datatype.parse(rawValue, locale);

                String formattedValue;
                if (valueFormatter.getFormatter() == null) {
                    formattedValue = datatype.format(datatypeValue, locale);
                } else {
                    formattedValue = valueFormatter.getFormatter().format(datatypeValue);
                }

                return datatype.parse(formattedValue, locale);
            } catch (ParseException ignored) {
                showValidationMessage();
                return prevValue;
            }
        }
        return rawValue;
    }

    protected void updateComponent(Object value) {
        getImpl().setText(valueFormatter.formatValue(value));
        getImpl().setCaretPosition(0);
        updateMissingValueState();
    }

    protected void fireChangeListeners() {
        fireChangeListeners(getValue());
    }

    protected void fireChangeListeners(Object newValue) {
        Object oldValue = prevValue;
        prevValue = newValue;
        if (!Objects.equals(oldValue, newValue)) {
            fireValueChanged(oldValue, newValue);
        }
    }

    protected void updateInstance(Object value) {
        if (updatingInstance)
            return;

        if (Objects.equals(prevValue, value)) {
            return;
        }

        updatingInstance = true;
        try {
            if (datasource != null && metaProperty != null && datasource.getItem() != null)
                InstanceUtils.setValueEx(datasource.getItem(), metaPropertyPath.getPath(), value);
        } finally {
            updatingInstance = false;
        }
    }

    protected void flush() {
        if (isEditable() && isEnabled()) {
            Object newValue = validateRawValue(getImpl().getText());
            if ("".equals(newValue))
                newValue = null;

            if (!Objects.equals(prevValue, newValue))
                setValue(newValue);
            else
                updateComponent(newValue);
        }
    }

    protected class TextFieldListener implements FocusListener, KeyListener {
        @Override
        public void focusGained(FocusEvent e) {
        }

        @Override
        public void focusLost(FocusEvent e) {
            flush();
        }

        @Override
        public void keyTyped(KeyEvent e) {
        }

        @Override
        public void keyPressed(KeyEvent e) {
            if (KeyEvent.VK_ENTER == e.getKeyCode() && e.getModifiers() == 0) {
                flush();
            }
        }

        @Override
        public void keyReleased(KeyEvent e) {
        }
    }
}