All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jaxx.runtime.validator.swing.SwingValidatorUtil Maven / Gradle / Ivy

There is a newer version: 3.0-alpha-6
Show newest version
/*
 * #%L
 * JAXX :: Validator
 * %%
 * Copyright (C) 2008 - 2014 Code Lutin, Tony Chemit
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */
package jaxx.runtime.validator.swing;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import jaxx.runtime.JAXXObject;
import jaxx.runtime.JAXXValidator;
import jaxx.runtime.SwingUtil;
import jaxx.runtime.validator.swing.meta.Validator;
import jaxx.runtime.validator.swing.meta.ValidatorField;
import jaxx.runtime.validator.swing.unified.UnifiedValidatorMessage;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.nuiton.util.ReflectUtil;
import org.nuiton.validator.NuitonValidatorScope;
import org.nuiton.validator.bean.list.BeanListValidator;
import org.nuiton.validator.bean.simple.SimpleBeanValidatorMessage;
import org.nuiton.validator.bean.simple.SimpleBeanValidators;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import java.awt.Color;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

import static org.nuiton.i18n.I18n.n;

/**
 * The helper class for swing validation module.
 *
 * @author Tony Chemit - [email protected]
 */
public class SwingValidatorUtil extends SimpleBeanValidators {

    /** Logger */
    static private final Log log = LogFactory.getLog(SwingValidatorUtil.class);

    protected static EnumMap icons;

    protected static EnumMap colors;

    public static EnumMap getIcons() {
        if (icons == null) {
            icons = new EnumMap(NuitonValidatorScope.class);
            icons.put(NuitonValidatorScope.FATAL, SwingUtil.createImageIcon("fatal.png"));
            icons.put(NuitonValidatorScope.ERROR, SwingUtil.createImageIcon("error.png"));
            icons.put(NuitonValidatorScope.WARNING, SwingUtil.createImageIcon("warning.png"));
            icons.put(NuitonValidatorScope.INFO, SwingUtil.createImageIcon("info.png"));
        }
        return icons;
    }

    public static EnumMap getColors() {
        if (colors == null) {
            colors = new EnumMap(NuitonValidatorScope.class);
            colors.put(NuitonValidatorScope.FATAL, Color.MAGENTA);
            colors.put(NuitonValidatorScope.ERROR, Color.RED);
            colors.put(NuitonValidatorScope.WARNING, Color.YELLOW);
            colors.put(NuitonValidatorScope.INFO, Color.GREEN);
        }
        return colors;
    }

    public static Color getColor(NuitonValidatorScope scope) {
        return scope == null ? null : getColors().get(scope);
    }

    public static ImageIcon getIcon(NuitonValidatorScope scope) {
        return scope == null ? null : getIcons().get(scope);
    }

    public static ImageIcon getFatalIcon() {
        return getIcons().get(NuitonValidatorScope.FATAL);
    }

    public static ImageIcon getErrorIcon() {
        return getIcons().get(NuitonValidatorScope.ERROR);
    }

    public static ImageIcon getWarningIcon() {
        return getIcons().get(NuitonValidatorScope.WARNING);
    }

    public static ImageIcon getInfoIcon() {
        return getIcons().get(NuitonValidatorScope.INFO);
    }

    protected SwingValidatorUtil() {
        // no instance
    }

    public static  SwingValidator newValidator(Class type,
                                                     String context) {

//        if (BeanValidatorFactory.isDefaultCreator()) {
//
//            // set the swing bean validator creator
//            BeanValidatorFactory.setCreator(new DefaultSwingValidatorCreator());
//        }
        return SwingValidator.newValidator(type, context);
    }

    /**
     * To install all the stuff for validation on a {@link JAXXValidator} ui.
     *
     * This method is called after validators has beeen detected in the ui (via
     * the method {@link #detectValidators(JAXXValidator)})..
     *
     * It will first find and register all validator field via the method
     * {@link JAXXValidator#registerValidatorFields()}, then for each
     * validators it will install ui for it (says connect validator to ui via layers)
     * and will reload attached bean to make visible bean validation state on ui.
     *
     * This method is always inovked by a generated jaxx-validator file at the
     * end of the {@code $completeSetup} method.
     *
     * @param ui the validator ui to init.
     */
    public static void installUI(JAXXValidator ui) {

        // first install fields with validation
        ui.registerValidatorFields();

//        detectValidatorFields(ui);

        // for each validator install uis + reload bean

        List validatorIds = ui.getValidatorIds();
        for (String validatorId : validatorIds) {
            SwingValidator validator = ui.getValidator(validatorId);

            // install uis
            validator.installUIs();

            // reload attached bean (to see validation on uis)
            validator.reloadBean();
        }
    }

    /**
     * Given a {@link JAXXValidator} ui, detects on it all the validators it
     * contains.
     *
     * A validator is detected from the annotation {@link Validator} placed on
     * his field.
     *
     * This method is always inovked by a generated jaxx-validator file at the
     * end of the {@code $completeSetup} method.
     *
     * @param ui the ui where to seek for validators.
     * @return the list of ids of validators found on the given ui
     */
    public static List detectValidators(JAXXValidator ui) {
        List validatorIds = new ArrayList();
        Map validators = ReflectUtil.getFieldAnnotation(
                ui.getClass(),
                Validator.class,
                true
        );

        for (Map.Entry entry : validators.entrySet()) {
            Field field = entry.getKey();
            Validator annotation = entry.getValue();
            String validatorId = annotation.validatorId();
            validatorIds.add(validatorId);
            if (log.isInfoEnabled()) {
                log.info("Detect validator [" + annotation.validatorId() +
                         "]  on field " + field.getName());
            }
        }
        return Collections.unmodifiableList(validatorIds);
    }

    /**
     * Detects on a {@link JAXXValidator} ui all the validator fields it
     * contains.
     *
     * A validator field is detected via the annotation placed on his field or
     * his getter (in cas of inheritance).
     *
     * Each field found will be registred to his corresponding validator via
     * the method {@link SwingValidator#setFieldRepresentation(String, JComponent)}.
     *
     * By default, this method is invoked in the generated method
     * {@link JAXXValidator#registerValidatorFields()} by a generated
     * jaxx-validator file.
     *
     * @param ui the ui to seek
     */
    public static void detectValidatorFields(JAXXValidator ui) {

        Multimap editors = getValidatorEditors(ui);

        try {

            for (String validatorId : ui.getValidatorIds()) {
                SwingValidator validator = ui.getValidator(validatorId);

                for (Map.Entry entry :
                        editors.entries()) {
                    ValidatorField fieldAnnotation = entry.getValue();
                    JComponent editor = entry.getKey();
                    if (!validatorId.equals(fieldAnnotation.validatorId())) {

                        // not good validator, skip this field
                        continue;
                    }
                    String[] propertyNames = fieldAnnotation.propertyName();
                    for (String propertyName : propertyNames) {
                        if (log.isInfoEnabled()) {
                            log.info("Detects for validator [" + validatorId +
                                     "] property " + propertyName +
                                     " for editor " + fieldAnnotation.editorName());
                        }
                        validator.setFieldRepresentation(propertyName, editor);
                    }
                }
            }

        } finally {

            editors.clear();
        }
    }

    /**
     * Prepare the ui where to display the validators messages.
     *
     * @param errorTable the table where to display validators messages
     * @param render     renderer to use
     */
    public static void installUI(JTable errorTable,
                                 SwingValidatorMessageTableRenderer render) {
        errorTable.setDefaultRenderer(Object.class, render);
        errorTable.getRowSorter().setSortKeys(
                Arrays.asList(new RowSorter.SortKey(0, SortOrder.ASCENDING)));
        SwingUtil.setI18nTableHeaderRenderer(
                errorTable,
                n("validator.scope.header"),
                n("validator.scope.header.tip"),
                n("validator.field.header"),
                n("validator.field.header.tip"),
                n("validator.message.header"),
                n("validator.message.header.tip"));
        // register a single 'goto widget error' mouse listener on errorTable
        registerErrorTableMouseListener(errorTable);
        SwingUtil.fixTableColumnWidth(errorTable, 0, 25);
    }

    /**
     * Prepare the ui where to display the validators messages.
     *
     * @param errorTable the table where to display simpleBean validators messages
     * @param render     renderer to use
     * @since 2.6.23
     */
    public static void installUI(JTable errorTable,
                                 SimpleBeanValidatorMessageTableRenderer render) {
        errorTable.setDefaultRenderer(Object.class, render);
        errorTable.getRowSorter().setSortKeys(
                Arrays.asList(new RowSorter.SortKey(0, SortOrder.ASCENDING)));
        SwingUtil.setI18nTableHeaderRenderer(
                errorTable,
                n("validator.scope.header"),
                n("validator.scope.header.tip"),
                n("validator.field.header"),
                n("validator.field.header.tip"),
                n("validator.message.header"),
                n("validator.message.header.tip"));
        SwingUtil.fixTableColumnWidth(errorTable, 0, 25);
    }

    /**
     * Prepare the ui where to display the validators messages.
     *
     * @param errorTable the table where to display validators messages
     * @param render     renderer to use
     * @since 2.5.3
     */
    public static void installUI(JTable errorTable,
                                 SwingListValidatorMessageTableRenderer render) {

        errorTable.setDefaultRenderer(Object.class, render);
        errorTable.getRowSorter().setSortKeys(
                Arrays.asList(new RowSorter.SortKey(0, SortOrder.ASCENDING)));
        SwingUtil.setI18nTableHeaderRenderer(
                errorTable,
                n("validator.scope.header"),
                n("validator.scope.header.tip"),
                n("validator.bean.header"),
                n("validator.bean.header.tip"),
                n("validator.field.header"),
                n("validator.field.header.tip"),
                n("validator.message.header"),
                n("validator.message.header.tip"));
        SwingUtil.fixTableColumnWidth(errorTable, 0, 25);

    }

    /**
     * Prepare the ui where to display the validators messages.
     *
     * @param errorTableModel
     * @param errorTable      the table where to display validators messages
     * @since 2.5.3
     */
    public static  void registerListValidator(BeanListValidator validator,
                                                 SwingListValidatorMessageTableModel errorTableModel,
                                                 JTable dataTable,
                                                 JTable errorTable,
                                                 SwingListValidatorDataLocator dataLocator) {

        // register the validator to the error table model
        errorTableModel.registerValidator(validator);


        // add click listener to go to cell
        errorTable.addMouseListener(new SwingListValidatorMessageTableMouseListener(
                dataTable,
                dataLocator
        ));

        // listen on editor model to add / remove bean into validator
        dataTable.getModel().addTableModelListener(
                new SwingListValidatorTableEditorModelListener(validator, dataLocator));
    }

    /**
     * Add hightlighters on the editor of beans.
     *
     * @param validator   the validator where to find bean states
     * @param editor      the editor of beans
     * @param dataLocator the data locator
     * @param scopes      scopes to hightlight
     * @param          type of bean to validate
     * @since 2.5.3
     */
    public static  void addHightLighterOnEditor(BeanListValidator validator,
                                                   JXTable editor,
                                                   SwingListValidatorDataLocator dataLocator,
                                                   NuitonValidatorScope... scopes) {

        for (NuitonValidatorScope scope : scopes) {

            SwingListValidatorHighlightPredicate predicate = SwingListValidatorHighlightPredicate.newPredicate(
                    scope,
                    validator, dataLocator
            );

            ColorHighlighter highlighter = new ColorHighlighter(predicate);
            highlighter.setBackground(SwingValidatorUtil.getColor(scope));
            editor.addHighlighter(highlighter);
        }
    }

    /**
     * Register for a given validator list ui a validator mouse listener.
     *
     * Note: there is only one listener registred for a given list model, so
     * invoking this method tiwce or more will have no effect.
     *
     * @param list the validation ui list
     * @return the listener instanciate or found
     * @see SwingValidatorMessageListMouseListener
     */
    public static SwingValidatorMessageListMouseListener registerErrorListMouseListener(JList list) {
        SwingValidatorMessageListMouseListener listener =
                getErrorListMouseListener(list);

        if (listener != null) {
            return listener;
        }
        listener = new SwingValidatorMessageListMouseListener();
        if (log.isDebugEnabled()) {
            log.debug(listener.toString());
        }
        list.addMouseListener(listener);
        return listener;
    }

    /**
     * Register for a given validator table ui a validator mouse listener
     *
     * Note: there is onlt one listener registred for a givne table model, so
     * invokin this method twice or more will have no effect.
     *
     * @param table the validator table ui
     * @return the listener instanciate or found
     * @see SwingValidatorMessageTableMouseListener
     */
    public static SwingValidatorMessageTableMouseListener registerErrorTableMouseListener(JTable table) {
        SwingValidatorMessageTableMouseListener listener =
                getErrorTableMouseListener(table);

        if (listener != null) {
            return listener;
        }
        listener = new SwingValidatorMessageTableMouseListener();
        if (log.isDebugEnabled()) {
            log.debug(listener.toString());
        }
        table.addMouseListener(listener);
        return listener;
    }

    /**
     * @param list the validator list ui
     * @return the validator list mouse listener, or null if not
     * found
     * @see SwingValidatorMessageListMouseListener
     */
    public static SwingValidatorMessageListMouseListener getErrorListMouseListener(JList list) {
        if (list != null) {
            for (MouseListener listener : list.getMouseListeners()) {
                if (listener instanceof SwingValidatorMessageListMouseListener) {
                    return (SwingValidatorMessageListMouseListener) listener;
                }
            }
        }
        return null;
    }

    /**
     * @param table the validator table ui
     * @return the validator table mouse listener, or null if not
     * found
     * @see SwingValidatorMessageTableMouseListener
     */
    public static SwingValidatorMessageTableMouseListener getErrorTableMouseListener(JTable table) {
        if (table != null) {
            for (MouseListener listener : table.getMouseListeners()) {
                if (listener instanceof SwingValidatorMessageTableMouseListener) {
                    return (SwingValidatorMessageTableMouseListener) listener;
                }
            }
        }
        return null;
    }

    /**
     * @param table the validator table ui
     * @return the validator table mouse listener, or null if not
     * found
     * @see SwingValidatorMessageTableMouseListener
     */
    public static SwingValidatorMessageTableMouseListener getListErrorTableMouseListener(JTable table) {
        if (table != null) {
            for (MouseListener listener : table.getMouseListeners()) {
                if (listener instanceof SwingValidatorMessageTableMouseListener) {
                    return (SwingValidatorMessageTableMouseListener) listener;
                }
            }
        }
        return null;
    }

    public static String getMessage(SwingValidatorMessage model) {
        String text = model.getMessage();
        if (model.getField() != null) {
            text = model.getI18nError(text);
        }
        return text;
    }

    public static String getMessage(SimpleBeanValidatorMessage model) {
        String text = model.getMessage();
        if (model.getField() != null) {
            text = model.getI18nError(text);
        }
        return text;
    }

    public static String getMessage(SwingListValidatorMessage model) {
        String text = model.getMessage();
        if (model.getField() != null) {
            text = model.getI18nError(text);
        }
        return text;
    }

    public static String getMessage(UnifiedValidatorMessage model) {
        String text = model.getMessage();
        if (model.getField() != null) {
            text = model.getI18nError(text);
        }
        return text;
    }

    public static String getFieldName(SwingValidatorMessage model, String value) {
        String text = null;
        JComponent editor = model.getEditor();
        if (editor != null) {
            text = (String) editor.getClientProperty("validatorLabel");
            /*if (l != null) {
            text = I18n.t(l);
            } else {
            // TODO should try the text
            }*/
        }
        if (text == null) {
            text = value;
        }
        return text;
    }

    public static String getFieldName(SwingListValidatorMessage model, String value) {
        String text = null;
        JComponent editor = model.getEditor();
        if (editor != null) {
            text = (String) editor.getClientProperty("validatorLabel");
            /*if (l != null) {
            text = I18n.t(l);
            } else {
            // TODO should try the text
            }*/
        }
        if (text == null) {
            text = value;
        }
        return text;
    }

    public static String getFieldName(UnifiedValidatorMessage model, String value) {
        return  getFieldName(model, value, null);
    }

    public static String getFieldName(UnifiedValidatorMessage model, String value, String valueFallBack) {
        String text = null;
        JComponent editor = model.getEditor();
        Object validatorLabel = null;
        if (editor != null) {

            validatorLabel = editor.getClientProperty("validatorLabel");

        }

        if (validatorLabel != null) {

            if (model.isSimpleValidator()) {

                text = (String) validatorLabel;

            } else {

                Map validatorLabelMap = (Map) validatorLabel;

                String field = model.getField();
                text = validatorLabelMap.get(field);

            }
        }

        if (text == null && valueFallBack != null) {
            text = valueFallBack;
        }

        if (text == null) {
            text = value;
        }

        return text;
    }

    /**
     * Method to listen the modification of the context name and at each time
     * reload fields of the ui.
     *
     * @param validator validator to listen
     * @param ui        ui to refresh when context name has changed
     * @since 2.2.1
     */
    public static void listenValidatorContextNameAndRefreshFields(
            SwingValidator validator,
            final JAXXValidator ui) {

        PropertyChangeListener listener = new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                SwingValidator validator = (SwingValidator) evt.getSource();
                if (log.isInfoEnabled()) {
                    log.info("Context name changed to [" + evt.getNewValue() +
                             "] for validator " + validator.getType());
                }
                ui.registerValidatorFields();
            }
        };
        validator.addPropertyChangeListener(
                SwingValidator.CONTEXT_PROPERTY,
                listener
        );
    }

//    /**
//     * Default bean validator creator to use in the {@link BeanValidatorFactory}.
//     *
//     * @author Tony Chemit - [email protected]
//     * @since 2.1
//     */
//    public static class DefaultSwingValidatorCreator implements BeanValidatorFactory.BeanValidatorCreator {
//
//        @Override
//        public  BeanValidator newBeanValidator(NuitonValidatorProvider provider, Class type, String context, NuitonValidatorScope... scopes) {
//            BeanValidator beanValidator = new SwingValidator(provider,
//                                                                   type,
//                                                                   context,
//                                                                   scopes
//            );
//            return beanValidator;
//        }
//    }

    /**
     * Convinient method to attach a bean to all validators of an JAXXObject.
     *
     * It is possible to exclude some validator to be treated.
     *
     * @param ui         the ui containing the validatros to treate
     * @param bean       the bean to attach in validators (can be null)
     * @param excludeIds the list of validator id to exclude
     */
    @SuppressWarnings({"unchecked"})
    public static void setValidatorBean(JAXXObject ui,
                                        Object bean,
                                        String... excludeIds) {
        if (!JAXXValidator.class.isAssignableFrom(ui.getClass())) {
            return;
        }
        JAXXValidator jaxxValidator = (JAXXValidator) ui;
        List validatorIds = jaxxValidator.getValidatorIds();
        if (excludeIds.length > 0) {
            validatorIds = new ArrayList(validatorIds);
            for (String excludeId : excludeIds) {
                validatorIds.remove(excludeId);
            }
        }
        for (String validatorId : validatorIds) {
            SwingValidator beanValidator =
                    jaxxValidator.getValidator(validatorId);
            if (bean == null || beanValidator.getType().isAssignableFrom(
                    bean.getClass())) {
                // touch validator, only if fits the bean type (or bean is null)
                beanValidator.setBean(bean);
            }
        }
    }

    /**
     * Convinient method to set the changed property to all validators of an
     * JAXXObject.
     *
     * It is possible to exclude some validator to be treated.
     *
     * @param ui         the ui containing the validatros to treate
     * @param newValue   the new value to set in changed validator property
     * @param excludeIds the list of validator id to exclude
     */
    @SuppressWarnings({"unchecked"})
    public static void setValidatorChanged(JAXXObject ui,
                                           boolean newValue,
                                           String... excludeIds) {
        if (!JAXXValidator.class.isAssignableFrom(ui.getClass())) {
            return;
        }
        JAXXValidator jaxxValidator = (JAXXValidator) ui;
        List validatorIds = jaxxValidator.getValidatorIds();
        if (excludeIds.length > 0) {
            validatorIds = new ArrayList(validatorIds);
            for (String excludeId : excludeIds) {
                validatorIds.remove(excludeId);
            }
        }
        for (String validatorId : validatorIds) {
            SwingValidator beanValidator =
                    jaxxValidator.getValidator(validatorId);
            beanValidator.setChanged(newValue);
        }
    }

    protected static Multimap getValidatorEditors(JAXXValidator ui) {

        Multimap editors = ArrayListMultimap.create();

        Map validatorFields = null;
        Map validatorMethods = null;
        try {

            validatorFields = ReflectUtil.getFieldAnnotation(
                    ui.getClass(),
                    ValidatorField.class,
                    true
            );

            validatorMethods = ReflectUtil.getMethodAnnotation(
                    ui.getClass(),
                    ValidatorField.class,
                    true
            );
            for (Map.Entry fieldEntry : validatorFields.entrySet()) {
                Field field = fieldEntry.getKey();
                field.setAccessible(true);
                ValidatorField fieldAnnotation = fieldEntry.getValue();
                JComponent editor = (JComponent) field.get(ui);
                editors.put(editor, fieldAnnotation);
            }

            for (Map.Entry fieldEntry : validatorMethods.entrySet()) {
                Method method = fieldEntry.getKey();
                method.setAccessible(true);
                ValidatorField fieldAnnotation = fieldEntry.getValue();
                JComponent editor = (JComponent) method.invoke(ui);
                editors.put(editor, fieldAnnotation);
            }

        } catch (Exception e) {
            throw new IllegalStateException("Could not init validators on ui " + ui, e);
        } finally {
            if (validatorFields != null) {
                validatorFields.clear();
            }
            if (validatorMethods != null) {
                validatorMethods.clear();
            }
        }
        return editors;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy