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

jaxx.runtime.validator.swing.SwingValidator 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.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import jaxx.runtime.validator.swing.ui.AbstractBeanValidatorUI;
import jaxx.runtime.validator.swing.ui.IconValidationUI;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdesktop.jxlayer.JXLayer;
import org.nuiton.validator.NuitonValidator;
import org.nuiton.validator.NuitonValidatorFactory;
import org.nuiton.validator.NuitonValidatorProvider;
import org.nuiton.validator.NuitonValidatorScope;
import org.nuiton.validator.bean.simple.SimpleBeanValidator;

import javax.swing.JComponent;
import java.awt.Container;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 * La surcharge de {@link SimpleBeanValidator} pour les ui swing
 *
 * Permet d'ajouter facilement le support de la validation des champs d'un bean
 * et de le relier a une interface graphique. Utilise xwork pour la validation
 * et JXLayer pour la visualisation.
 *
 *
 * Le mieux pour son integration dans Jaxx est de faire de la generation pour
 * force la compilation du code suivant:
 *
 * 
 * myValidor.getBean().get<field>();
 * 
* * et ceci pour chaque field ajoute a la map fieldRepresentation. De cette facon * meme si le champs field est en texte on a une verification de son existance a * la compilation. * * * La representation en tag pourrait etre *
 * <validator id="myValidator" beanClass="{Personne.class}"
 * errorList="$list">
 *   <field name="name" component="$name"/>
 *   <field name="firstName" component="$firstName"/>
 *   <field name="birthDate" component="$birthDate"/>
 * </validator>
 * <validator beanClass="{Personne.class}" autoField="true"
 * errorList="$list">
 *   <fieldRepresentation name="name" component="$lastName"/>
 * </validator>
 * 
* * dans le premier exemple on fait un mapping explicite des champs, mais on voit * que le nom du composant graphique est le meme que celui du champs. Pour * eviter de longue saisie, il est possible d'utiliser le flag autoField * qui pour chaque champs du ayant une methode get du bean recherche un * composant avec cet Id. Il est aussi possible de surcharge un champs * explicitement comme ici name, dans le cas ou le composant qui porterait ce * nom serait utilise pour autre chose. * * * Il faut un handler particulier pour ce composant car les attributs * beanClass et autoField ne sont present que dans le XML jaxx et * servent a la generation. Il faut aussi prendre en compte les elements * fieldRepresentation fils du tag validator. * * * Voici ce que pourrait etre le code genere par jaxx *
 * // declaration du bean
 * BeanValidator<beanClass> $myValidator;
 * // init du bean
 * protected void createMyValidator() {
 *   $myValidator = new BeanValidator<beanClass>();
 *   // genere seulement si autoField = true
 *   for (Method m : beanClass.getMethod()) {
 *     if (m.getName().startsWith("get")) {
 *       String fieldName = m.getName().substring(3).toLowerCase();
 *       $myValidator.setFieldRepresentation(fieldName,
 * $objectMap.get(fieldName));
 *     }
 *   }
 *   // pour chaque tag fieldRepresentation
 *   myValidator.setFieldRepresentation("name", $lastName);
 *   // si beanClass est specifie et n'est pas Object, on force l'acces au
 * champs
 *   // pour validation a la compilation
 *   $myValidator.getBean().getName();
 *   $objectMap.put("myValidator", $myValidator);
 * }
 * 
* * @param le type de bean a valider * @author Tony Chemit - [email protected] * @version 1.0 */ public class SwingValidator extends SimpleBeanValidator { /** Logger */ private static final Log log = LogFactory.getLog(SwingValidator.class); private static final Class DEFAULT_UI_CLASS = IconValidationUI.class; /** * Obtain a new {@link SimpleBeanValidator} for the given parameters. * * Note: It will use the default provider of {@link NuitonValidator} * * @param type type of bean to validate * @param context context of validation * @param scopes authorized scopes (if {@code null}, will use all scopes) * @param type of bean to validate * @return the new instanciated {@link SimpleBeanValidator}. * @throws NullPointerException if type is {@code null} * @see NuitonValidatorFactory#getDefaultProviderName() */ public static SwingValidator newValidator( Class type, String context, NuitonValidatorScope... scopes) throws NullPointerException { // get the provider default name String providerName = NuitonValidatorFactory.getDefaultProviderName(); // get the bean validator with this provider return newValidator(providerName, type, context, scopes ); } /** * Obtain a new {@link SimpleBeanValidator} for the given parameters. * * Note: It will use the provider of {@link NuitonValidator} * defined by the {@code providerName}. * * @param providerName name of {@link NuitonValidator} to use * @param type type of bean to validate * @param context context of validation * @param scopes authorized scopes (if {@code null}, will use all scopes) * @param type of bean to validate * @return the new instanciated {@link SimpleBeanValidator}. * @throws NullPointerException if type is {@code null} * @see NuitonValidatorFactory#getProvider(String) */ public static SwingValidator newValidator( String providerName, Class type, String context, NuitonValidatorScope... scopes) throws NullPointerException { Preconditions.checkNotNull(type, "type parameter can not be null."); // get delegate validator provider NuitonValidatorProvider provider = NuitonValidatorFactory.getProvider(providerName); Preconditions.checkState( provider != null, "Could not find provider with name " + providerName); // create the new instance of bean validator return new SwingValidator( provider, type, context, scopes); } /** * permet de faire le lien en un champs du bean et l'objet qui permet de * l'editer */ protected final Map fieldRepresentation; /** Object servant a contenir la liste des erreurs */ protected SwingValidatorMessageListModel errorListModel; /** Object servant a contenir la liste des erreurs */ protected SwingValidatorMessageTableModel errorTableModel; /** ui renderer class */ protected Class uiClass; public SwingValidator(NuitonValidatorProvider provider, Class beanClass, String contextName, NuitonValidatorScope... filterScopes) { super(provider, beanClass, contextName, filterScopes); fieldRepresentation = new HashMap(); } public SwingValidator(Class beanClass, String contextName, NuitonValidatorScope... filterScopes) { super(NuitonValidatorFactory.getDefaultProvider(), beanClass, contextName, filterScopes ); fieldRepresentation = new HashMap(); } public SwingValidator(Class beanClass, String contextName) { this(beanClass, contextName, NuitonValidatorScope.values()); } /** * To reload a bean in the validator. * * This method is used to reload ui, since some editors could not exist when * validator is init, so some messages should not be attached to an editor. */ public void reloadBean() { B b = getBean(); if (log.isInfoEnabled()) { log.info("Will reload bean : " + b); } if (b != null) { setBean(null); setBean(b); } } public JComponent getFieldRepresentation(String fieldname) { return fieldRepresentation.get(fieldname); } public Class getUiClass() { return uiClass; } public void setErrorListModel( SwingValidatorMessageListModel errorListModel) { this.errorListModel = errorListModel; if (errorListModel != null) { // register the validator in the model list errorListModel.registerValidator(this); } } public void setErrorTableModel( SwingValidatorMessageTableModel errorTableModel) { this.errorTableModel = errorTableModel; if (errorTableModel != null) { // register the validator in the model table errorTableModel.registerValidator(this); } } public void setUiClass(Class uiClass) { this.uiClass = uiClass; } @Override public void setContext(String context) { String oldContext = getContext(); super.setContext(context); if (context == null && oldContext == null || context != null && context.equals(oldContext)) { // same context do nothing return; } if (fieldRepresentation != null) { // must reinstall ui installUIs(); } } /** * Permet d'indiquer le composant graphique responsable de l'affichage d'un * attribut du bean * * @param fieldname the field name in the bean * @param c the editor component for the field */ public void setFieldRepresentation(String fieldname, JComponent c) { boolean fieldFound = getDelegate().getEffectiveFields().contains(fieldname); if (!fieldFound) { // no field registred in the validator if (log.isWarnEnabled()) { log.warn("the field '" + fieldname + "' is not defined in validator (no rules on it)"); } } else { if (log.isInfoEnabled()) { log.info("register field [" + fieldname + "] with component : " + c.getName() ); } fieldRepresentation.put(fieldname, c); } } public void setFieldRepresentation( Map fieldRepresentation) { for (Map.Entry e : fieldRepresentation.entrySet()) { setFieldRepresentation(e.getKey(), e.getValue()); } } @Override public SwingValidator getParentValidator() { return (SwingValidator) super.getParentValidator(); } public void setParentValidator(SwingValidator parentValidator) { super.setParentValidator(parentValidator); } /** install ui on required components */ public void installUIs() { if (fieldRepresentation == null) { throw new NullPointerException( "fieldRepresentation is null, must init before " + "invoking installUIs method..."); } if (uiClass == null) { // use the default one uiClass = DEFAULT_UI_CLASS; } // compute reverse map of fieldRepresentation : a same editor can // reference more than one field Multimap fieldsByEditor = ArrayListMultimap.create(); for (Entry entry : fieldRepresentation.entrySet()) { fieldsByEditor.put(entry.getValue(), entry.getKey()); } for (JComponent editor : fieldsByEditor.keySet()) { Collection fields = fieldsByEditor.get(editor); try { setMessageRepresentation( editor, fields, uiClass ); } catch (Exception e) { throw new RuntimeException(e); } } // for (Entry entry : // fieldRepresentation.entrySet()) { // try { // setMessageRepresentation( // entry.getKey(), // null, // entry.getValue(), // uiClass // ); // } catch (Exception e) { // throw new RuntimeException(e); // } // } } protected void setMessageRepresentation( JComponent c, Collection fieldnames, Class uiClass) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException { Preconditions.checkNotNull(c, "No editor"); // ajout du jxlayer sous ce composant Container container = c.getParent(); if (container instanceof JXLayer) { JXLayer jx = (JXLayer) container; Object oldUI = jx.getUI(); if (oldUI != null && oldUI instanceof AbstractBeanValidatorUI) { // supression de l'ancien layer removeSimpleBeanValidatorListener((AbstractBeanValidatorUI) oldUI); } Constructor cons = uiClass.getConstructor(Collection.class); AbstractBeanValidatorUI ui = cons.newInstance(fieldnames); // ui.setEnabled(true); addSimpleBeanValidatorListener(ui); jx.setUI(ui); } } @Deprecated protected void setMessageRepresentation( String fieldname, JComponent old, JComponent c, Class uiClass) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException { if (old == c) { // same component, nothing to do return; } boolean fieldFound = getDelegate().getEffectiveFields().contains(fieldname); if (!fieldFound) { // this case should not appear since fieldName has already been // check in method addFieldRepresentation return; } if (old != null) { // suppression du jxlayer sous l'ancien composant Container container = old.getParent(); if (container instanceof JXLayer) { JXLayer jx = (JXLayer) container; Object ui = jx.getUI(); if (ui != null && ui instanceof AbstractBeanValidatorUI) { removeSimpleBeanValidatorListener((AbstractBeanValidatorUI) ui); } jx.setUI(null); } } if (c != null) { // ajout du jxlayer sous ce composant Container container = c.getParent(); if (container instanceof JXLayer) { JXLayer jx = (JXLayer) container; Object oldUI = jx.getUI(); if (oldUI != null && oldUI instanceof AbstractBeanValidatorUI) { // supression de l'ancien layer removeSimpleBeanValidatorListener((AbstractBeanValidatorUI) oldUI); } Constructor cons = uiClass.getConstructor(String.class); AbstractBeanValidatorUI ui = cons.newInstance(fieldname); // ui.setEnabled(true); addSimpleBeanValidatorListener(ui); jx.setUI(ui); } } } }