Author: tchemit Date: 2008-10-17 20:55:15 +0000 (Fri, 17 Oct 2008) New Revision: 958 Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidator.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidatorError.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidatorErrorListModel.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ui/ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ui/AbstractBeanValidatorUI.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ui/IconValidationUI.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ui/TranslucentValidationUI.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/BeanValidatorHandler.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/FieldValidatorHandler.java Removed: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidator.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidatorError.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidatorErrorListModel.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/BeanValidatorHandler.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/FieldValidatorHandler.java Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompiler.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXValidator.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/SwingInitializer.java Log: add package validator introduce ui package (to put error rendering) Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompiler.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompiler.java 2008-10-17 19:44:04 UTC (rev 957) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompiler.java 2008-10-17 20:55:15 UTC (rev 958) @@ -22,7 +22,7 @@ import jaxx.tags.DefaultObjectHandler; import jaxx.tags.TagHandler; import jaxx.tags.TagManager; -import jaxx.tags.swing.BeanValidatorHandler.CompiledBeanValidator; +import jaxx.tags.validator.BeanValidatorHandler.CompiledBeanValidator; import jaxx.types.TypeManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -610,7 +610,7 @@ String id = TypeManager.getJavaCode(validator.getId()); code.append("validatorIds.add(").append(id).append(");"); code.append(getLineSeparator()); - code.append("getValidator(").append(id).append(").installLayers();"); + code.append("getValidator(").append(id).append(").installUIs();"); code.append("getValidator(").append(id).append(").validate();"); code.append(getLineSeparator()); } @@ -695,7 +695,7 @@ javaFile.addField(new JavaField(Modifier.PROTECTED, "java.util.Map<String,Object>", "$bindingSources", "new HashMap<String,Object>()")); } - javaFile.addImport("jaxx.runtime.BeanValidator"); + javaFile.addImport("jaxx.runtime.validator.BeanValidator"); javaFile.addField(new JavaField(Modifier.PROTECTED, "java.util.List<String>", "validatorIds", "new ArrayList<String>()")); if (stylesheet != null) { Deleted: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidator.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidator.java 2008-10-17 19:44:04 UTC (rev 957) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidator.java 2008-10-17 20:55:15 UTC (rev 958) @@ -1,484 +0,0 @@ -/* *##% - * Copyright (C) 2002-2008 Code Lutin, Benjamin Poussin - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * 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 Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - *##%*/ - -package jaxx.runtime; - - -import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.ValidationAware; -import com.opensymphony.xwork2.ValidationAwareSupport; -import com.opensymphony.xwork2.config.Configuration; -import com.opensymphony.xwork2.config.ConfigurationManager; -import com.opensymphony.xwork2.util.ValueStack; -import com.opensymphony.xwork2.util.ValueStackFactory; -import com.opensymphony.xwork2.validator.ActionValidatorManager; -import com.opensymphony.xwork2.validator.DelegatingValidatorContext; -import com.opensymphony.xwork2.validator.ValidationException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.jdesktop.jxlayer.JXLayer; -import org.jdesktop.jxlayer.plaf.AbstractLayerUI; - -import javax.swing.BorderFactory; -import javax.swing.JComponent; -import javax.swing.SwingUtilities; -import java.awt.AlphaComposite; -import java.awt.Color; -import java.awt.Container; -import java.awt.Graphics2D; -import java.awt.Insets; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.image.BufferedImage; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -/** - * <p/> - * 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. - * <p/> - * <p/> - * Le mieux pour son integration dans Jaxx est de faire de la generation pour - * force la compilation du code suivant: - * <p/> - * <pre> - * myValidor.getBean().get<field>(); - * </pre> - * <p/> - * 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. - * <p/> - * <p/> - * La representation en tag pourrait etre - * <pre> - * <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> - * </pre> - * <p/> - * 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 <b>autoField</b> - * 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. - * <p/> - * <p/> - * Il faut un handler particulier pour ce composant car les attributs - * <b>beanClass</b> et <b>autoField</b> 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. - * <p/> - * <p/> - * Voici ce que pourrait etre le code genere par jaxx - * <pre> - * // 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); - * } - * </pre> - * - * @author poussin - * @version $Revision$ - * <p/> - * Last update: $Date$ - * by : $Author$ - */ -public class BeanValidator<T> { - - /** to use log facility, just put in your code: log.info(\"...\"); */ - static private Log log = LogFactory.getLog(BeanValidator.class); - - protected PropertyChangeSupport pcs; - - protected ValidationAwareSupport validationSupport; - protected DelegatingValidatorContext validationContext; - - protected transient ActionValidatorManager validator; - - /** indique si le bean a ete modifie depuis son arrivee */ - protected boolean changed = false; - - /** le bean a surveiller */ - protected T bean = null; - - /** l'objet qui recoit les notifications de modification du bean */ - protected Listener l = new Listener(); - - /** permet de faire le lien en un champs du bean et l'objet qui permet de l'editer */ - protected Map<String, JComponent> fieldRepresentation; - - /** Object servant a contenir la liste des erreurs */ - protected BeanValidatorErrorListModel errorListModel; - - public BeanValidator() { - pcs = new PropertyChangeSupport(this); - validationSupport = new ValidationAwareSupport(); - validationContext = new DelegatingValidatorContext(validationSupport); - fieldRepresentation = new HashMap<String, JComponent>(); - } - - public BeanValidatorErrorListModel getErrorListModel() { - return errorListModel; - } - - public void setErrorListModel(BeanValidatorErrorListModel errorListModel) { - this.errorListModel = errorListModel; - if (errorListModel != null) { - errorListModel.registerValidator(this); - } - } - - /** - * 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) { - JComponent old = fieldRepresentation.put(fieldname, c); - setErrorRepresentation(fieldname, old, c); - } - - public void setFieldRepresentation(Map<String, JComponent> fieldRepresentation) { - for (Map.Entry<String, JComponent> e : fieldRepresentation.entrySet()) { - setFieldRepresentation(e.getKey(), e.getValue()); - } - } - - public JComponent getFieldRepresentation(String fieldname) { - return fieldRepresentation.get(fieldname); - } - - /** - * Retourne vrai si l'objet bean a ete modifie depuis le dernier - * {@link #setBean} - * - * @return <code>true</code> if bean was modify since last {@link #setBean(Object)} invocation - */ - public boolean isChanged() { - return changed; - } - - /** - * Permet de force la remise a false de l'etat de changement du bean - * - * @param changed flag to force reset of property {@link #changed} - */ - public void setChanged(boolean changed) { - boolean oldChanged = this.changed; - this.changed = changed; - pcs.firePropertyChange("changed", oldChanged, changed); - } - - public T getBean() { - return bean; - } - - public void setBean(T bean) { - T oldBean = this.bean; - if (this.bean != null) { - try { - Method method = this.bean.getClass().getMethod("removePropertyChangeListener", PropertyChangeListener.class); - method.invoke(this.bean, l); - } catch (Exception eee) { - log.info("Can't register as listener", eee); - } - } - this.bean = bean; - if (this.bean != null) { - try { - Method method = this.bean.getClass().getMethod("addPropertyChangeListener", PropertyChangeListener.class); - method.invoke(this.bean, l); - } catch (Exception eee) { - log.info("Can't register as listener", eee); - } - } - validate(); - pcs.firePropertyChange("bean", oldBean, bean); - } - - public Map getFieldErrors() { - return validationContext.getFieldErrors(); - } - - public Collection getActionErrors() { - return validationContext.getActionErrors(); - } - - public Collection getActionMessages() { - return validationContext.getActionMessages(); - } - - /** install layers on required components */ - public void installLayers() { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - for (String fieldname : fieldRepresentation.keySet()) { - JComponent c = fieldRepresentation.get(fieldname); - setErrorRepresentation(fieldname, null, c); - } - } - }); - - } - - protected ActionValidatorManager getValidator() { - if (validator == null) { - ConfigurationManager confManager = new ConfigurationManager(); - Configuration conf = confManager.getConfiguration(); - - ValueStackFactory vsf = conf.getContainer().getInstance( - ValueStackFactory.class); - ValueStack vs = vsf.createValueStack(); - ActionContext context = new ActionContext(vs.getContext()); - ActionContext.setContext(context); - - validator = conf.getContainer().getInstance( - ActionValidatorManager.class, "no-annotations"); - } - return validator; - } - - /** - * il faut eviter le code re-intrant (durant une validation, une autre est - * demandee). Pour cela on fait la validation dans un thread, et tant - * que la premiere validation n'est pas fini, on ne repond pas aux - * solicitations. - * Cette method est public pour permettre de force une validation par - * programmation, ce qui est utile par exemple si le bean ne supporte - * pas les {@link PropertyChangeListener} - */ - public void validate() { - // on ne valide que si il y a un bean et que le resultat de la validation - // pourra etre affiche quelque part - if (bean == null || getErrorListModel() == null || fieldRepresentation.size() == 0) { - return; - } - - try { - - validationSupport.clearErrorsAndMessages(); - - getValidator().validate(bean, null, validationContext); - - if (log.isDebugEnabled()) { - log.debug("Action errors: " + validationContext.getActionErrors()); - log.debug("Action messages: " + validationContext.getActionMessages()); - log.debug("Field errors: " + validationContext.getFieldErrors()); - } - - log.info(this + " : " + validationContext.getFieldErrors()); - - // add errors - errorListModel.addErrors(this); - - for (String fieldname : fieldRepresentation.keySet()) { - JComponent c = fieldRepresentation.get(fieldname); - //todo: a supprimer mais actuellemnt le layer ne se repaint pas bien, cela n'est pas normal - c.getParent().repaint(); - } - } catch (ValidationException eee) { - log.warn("Error during validation", eee); - } - } - - protected void setErrorRepresentation(String fieldname, JComponent old, JComponent c) { - if (old != c) { - if (old != null) { - // suppression du jxlayer sous l'ancien composant - Container container = old.getParent(); - if (container instanceof JXLayer) { - JXLayer<?> jx = (JXLayer<?>) container; - jx.setUI(null); - } - } - if (c != null) { - // ajout du jxlayer sous ce composant - Container container = c.getParent(); - if (container instanceof JXLayer) { - IconValidationUI ui = new IconValidationUI(fieldname, c.getName(), validationSupport); - //TranslucentValidationUI ui = new TranslucentValidationUI(fieldname, c.getName(), validationSupport); - ui.setEnabled(true); - JXLayer<JComponent> jx = (JXLayer<JComponent>) container; - jx.setUI(ui); - } - } - } - } - - protected class Listener implements PropertyChangeListener { - - public void propertyChange(PropertyChangeEvent evt) { - // only validate if the property has really changed... - Object oldValue = evt.getOldValue(); - Object newValue = evt.getNewValue(); - if (oldValue == null && newValue == null) { - return; - } - if ((oldValue != null && oldValue.equals(newValue)) || (newValue != null && newValue.equals(oldValue))) { - return; - } - if (log.isDebugEnabled()) { - log.debug("launch validation from event [name:" + evt.getPropertyName() + " <old:" + oldValue + ", new:" + newValue + ">]"); - } - validate(); - setChanged(true); - } - } - - /** @return <code>true</code> if errors are detected, <code>false</code> otherwise */ - public boolean hasErrors() { - //todo should also detecte actionErrors ? - return validationContext.hasFieldErrors(); - //return validationContext.hasFieldErrors() || validationContext.hasActionErrors(); - } - - public static class TranslucentValidationUI extends AbstractLayerUI<JComponent> { - - /** bean field */ - protected String field; - - /** component id */ - protected String componentId; - - /** validation context */ - protected ValidationAware validationContext; - - public TranslucentValidationUI(String field, String componentId, ValidationAware validationContext) { - this.field = field; - this.componentId = componentId; - this.validationContext = validationContext; - log.info("install " + this + "<field:" + field + ", componentId:" + componentId + ">"); - } - - @Override - protected void paintLayer(Graphics2D g2, JXLayer<JComponent> l) { - // paints the layer as is - super.paintLayer(g2, l); - - // to be in sync with the view if the layer has a border - Insets layerInsets = l.getInsets(); - g2.translate(layerInsets.left, layerInsets.top); - - JComponent view = l.getView(); - // To prevent painting on view's border - Insets insets = view.getInsets(); - g2.clip(new Rectangle(insets.left, insets.top, - view.getWidth() - insets.left - insets.right, - view.getHeight() - insets.top - insets.bottom)); - - g2.setColor(!validationContext.getFieldErrors().containsKey(field) ? - Color.GREEN : Color.RED); - g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .2f)); - g2.fillRect(0, 0, l.getWidth(), l.getHeight()); - } - } - - public static class IconValidationUI extends AbstractLayerUI<JComponent> { - - // The red icon to be shown at the layer's corner - private final static BufferedImage INVALID_ICON; - - static { - int width = 7; - int height = 8; - INVALID_ICON = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - Graphics2D g2 = (Graphics2D) INVALID_ICON.getGraphics(); - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); - g2.setColor(Color.RED); - g2.fillRect(0, 0, width, height); - g2.setColor(Color.WHITE); - g2.drawLine(0, 0, width, height); - g2.drawLine(0, height, width, 0); - g2.dispose(); - } - - /** bean validation field */ - protected String field; - - /** component id */ - protected String componentId; - - /** validation context */ - protected ValidationAware validationContext; - - public IconValidationUI(String field, String componentId, ValidationAware validationContext) { - this.field = field; - this.componentId = componentId; - this.validationContext = validationContext; - log.info("install " + this + "<field:" + field + ", componentId:" + componentId + ">"); - } - - @Override - public void installUI(JComponent c) { - super.installUI(c); - c.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 3)); - } - - @Override - public void uninstallUI(JComponent c) { - super.uninstallUI(c); - c.setBorder(null); - } - - @Override - protected void paintLayer(Graphics2D g2, JXLayer<JComponent> l) { - super.paintLayer(g2, l); - // There is no need to take insets into account for this painter - if (validationContext.getFieldErrors().containsKey(field)) { - g2.drawImage(INVALID_ICON, l.getWidth() - INVALID_ICON.getWidth() - 1, 0, null); - // g2.drawImage(INVALID_ICON, 0, 0, null); - } - } - } - - -} Deleted: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidatorError.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidatorError.java 2008-10-17 19:44:04 UTC (rev 957) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidatorError.java 2008-10-17 20:55:15 UTC (rev 958) @@ -1,83 +0,0 @@ -package jaxx.runtime; - -import org.codelutin.i18n.I18n; - -import javax.swing.JComponent; - -/** - * The model of an error. - * - * @author chemit - */ -public class BeanValidatorError<T> { - - protected BeanValidator<T> validator; - - protected String fieldName; - - protected String error; - - protected JComponent component; - - public BeanValidator<T> getValidator() { - return validator; - } - - public String getFieldName() { - return fieldName; - } - - public String getError() { - return error; - } - - public JComponent getComponent() { - return component; - } - - public void setValidator(BeanValidator<T> validator) { - this.validator = validator; - } - - public void setFieldName(String fieldName) { - this.fieldName = fieldName; - } - - public void setError(String error) { - this.error = error; - } - - public void setComponent(JComponent component) { - this.component = component; - } - - @Override - public String toString() { - return component.getName() + " : " + I18n._(error); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof BeanValidatorError)) { - return false; - } - - BeanValidatorError that = (BeanValidatorError) o; - - return component.equals(that.component) && error.equals(that.error) && fieldName.equals(that.fieldName) && validator.equals(that.validator); - - } - - @Override - public int hashCode() { - int result; - result = validator.hashCode(); - result = 31 * result + fieldName.hashCode(); - result = 31 * result + error.hashCode(); - result = 31 * result + component.hashCode(); - return result; - } -} Deleted: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidatorErrorListModel.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidatorErrorListModel.java 2008-10-17 19:44:04 UTC (rev 957) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidatorErrorListModel.java 2008-10-17 20:55:15 UTC (rev 958) @@ -1,138 +0,0 @@ -package jaxx.runtime; - -import javax.swing.DefaultListModel; -import javax.swing.JComponent; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; - -/** - * The model of the list of errors - * - * @author chemit - */ -public class BeanValidatorErrorListModel extends DefaultListModel { - private static final long serialVersionUID = 1L; - - /** list of registred validators */ - protected transient List<BeanValidator<?>> validators; - - /** comporatorof errors */ - protected transient Comparator<BeanValidatorError> comparator; - - public BeanValidatorErrorListModel() { - validators = new ArrayList<BeanValidator<?>>(); - } - - public void registerValidator(BeanValidator<?> validator) { - if (validators.contains(validator)) { - throw new IllegalArgumentException("the validator " + validator + " is already registred in " + this); - } - validators.add(validator); - } - - public <T> void addErrors(BeanValidator<T> validator) { - if (!validators.contains(validator)) { - throw new IllegalArgumentException("the validator " + validator + " is not registred in " + this); - } - - // new errors for the given validator - List<BeanValidatorError> newErrors = new ArrayList<BeanValidatorError>(); - // old errors for the given validator - List<BeanValidatorError> oldErrors = new ArrayList<BeanValidatorError>(); - // old errors for other validators - List<BeanValidatorError> oldOtherErrors = new ArrayList<BeanValidatorError>(); - - // split old errors from other validators - splitOldErrors(validator, oldErrors, oldOtherErrors); - - // fill new errors - fillNewErrors(validator, newErrors); - - // check if something has changed for the given validator - if (hasChanged(oldErrors, newErrors)) { - - // reinject other errors - newErrors.addAll(oldOtherErrors); - - // sort errors - Collections.sort(newErrors, getComparator()); - - // clean errors in model - clear(); - - // reinject in list model, all the errors - for (BeanValidatorError error : newErrors) { - addElement(error); - } - - // notify thaht the model has changed - fireContentsChanged(this, 0, getSize() - 1); - } - } - - protected boolean hasChanged(List<BeanValidatorError> oldErrors, List<BeanValidatorError> newErrors) { - if (oldErrors.size() != newErrors.size()) { - return true; - } - for (BeanValidatorError oldError : oldErrors) { - if (!newErrors.contains(oldError)) { - return true; - } - } - return false; - } - - protected <T> void fillNewErrors(BeanValidator<T> validator, List<BeanValidatorError> newErrors) { - if (validator.hasErrors()) { - // inject this validator errors - for (Object o : validator.getFieldErrors().entrySet()) { - Map.Entry<?, ?> r = (Map.Entry<?, ?>) o; - BeanValidatorError<T> error; - String field = (String) r.getKey(); - - JComponent component = validator.getFieldRepresentation(field); - for (Object errorString : (List<?>) r.getValue()) { - String error1 = (String) errorString; - error = new BeanValidatorError<T>(); - error.setComponent(component); - error.setValidator(validator); - error.setError(error1); - error.setFieldName(field); - newErrors.add(error); - } - } - //todo should also do actionErrors - } - } - - protected <T> void splitOldErrors(BeanValidator<T> validator, List<BeanValidatorError> oldErrors, List<BeanValidatorError> oldOtherErrors) { - Enumeration enumeration = elements(); - - while (enumeration.hasMoreElements()) { - Object o = enumeration.nextElement(); - BeanValidatorError<?> error = (BeanValidatorError) o; - if (!error.getValidator().equals(validator)) { - // error from another validator, keep it - oldOtherErrors.add(error); - } else { - oldErrors.add(error); - } - } - } - - protected Comparator<BeanValidatorError> getComparator() { - if (comparator == null) { - comparator = new Comparator<BeanValidatorError>() { - public int compare(BeanValidatorError o1, BeanValidatorError o2) { - return o1.toString().compareTo(o2.toString()); - } - }; - } - return comparator; - } - -} Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXValidator.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXValidator.java 2008-10-17 19:44:04 UTC (rev 957) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXValidator.java 2008-10-17 20:55:15 UTC (rev 958) @@ -1,5 +1,7 @@ package jaxx.runtime; +import jaxx.runtime.validator.BeanValidator; + import java.util.List; /** Copied: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidator.java (from rev 957, lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidator.java) =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidator.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidator.java 2008-10-17 20:55:15 UTC (rev 958) @@ -0,0 +1,402 @@ +/* *##% + * Copyright (C) 2002-2008 Code Lutin, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + *##%*/ + +package jaxx.runtime.validator; + + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ValidationAware; +import com.opensymphony.xwork2.ValidationAwareSupport; +import com.opensymphony.xwork2.config.Configuration; +import com.opensymphony.xwork2.config.ConfigurationManager; +import com.opensymphony.xwork2.util.ValueStack; +import com.opensymphony.xwork2.util.ValueStackFactory; +import com.opensymphony.xwork2.validator.ActionValidatorManager; +import com.opensymphony.xwork2.validator.DelegatingValidatorContext; +import com.opensymphony.xwork2.validator.ValidationException; +import jaxx.runtime.validator.ui.AbstractBeanValidatorUI; +import jaxx.runtime.validator.ui.IconValidationUI; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jdesktop.jxlayer.JXLayer; + +import javax.swing.JComponent; +import javax.swing.SwingUtilities; +import java.awt.Container; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * <p/> + * 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. + * <p/> + * <p/> + * Le mieux pour son integration dans Jaxx est de faire de la generation pour + * force la compilation du code suivant: + * <p/> + * <pre> + * myValidor.getBean().get<field>(); + * </pre> + * <p/> + * 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. + * <p/> + * <p/> + * La representation en tag pourrait etre + * <pre> + * <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> + * </pre> + * <p/> + * 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 <b>autoField</b> + * 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. + * <p/> + * <p/> + * Il faut un handler particulier pour ce composant car les attributs + * <b>beanClass</b> et <b>autoField</b> 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. + * <p/> + * <p/> + * Voici ce que pourrait etre le code genere par jaxx + * <pre> + * // 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); + * } + * </pre> + * + * @author poussin + * @version $Revision$ + * <p/> + * Last update: $Date$ + * by : $Author$ + */ +public class BeanValidator<T> { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private final Log log = LogFactory.getLog(BeanValidator.class); + + static private final Class<? extends AbstractBeanValidatorUI> DEFAULT_UI_CLASS = IconValidationUI.class; + + protected PropertyChangeSupport pcs; + + protected ValidationAwareSupport validationSupport; + protected DelegatingValidatorContext validationContext; + + protected transient ActionValidatorManager validator; + + /** indique si le bean a ete modifie depuis son arrivee */ + protected boolean changed = false; + + /** le bean a surveiller */ + protected T bean = null; + + /** l'objet qui recoit les notifications de modification du bean */ + protected Listener l = new Listener(); + + /** permet de faire le lien en un champs du bean et l'objet qui permet de l'editer */ + protected Map<String, JComponent> fieldRepresentation; + + /** Object servant a contenir la liste des erreurs */ + protected BeanValidatorErrorListModel errorListModel; + + protected Class<? extends AbstractBeanValidatorUI> uiClass; + + public BeanValidator() { + pcs = new PropertyChangeSupport(this); + validationSupport = new ValidationAwareSupport(); + validationContext = new DelegatingValidatorContext(validationSupport); + fieldRepresentation = new HashMap<String, JComponent>(); + } + + public BeanValidatorErrorListModel getErrorListModel() { + return errorListModel; + } + + public void setErrorListModel(BeanValidatorErrorListModel errorListModel) { + this.errorListModel = errorListModel; + if (errorListModel != null) { + errorListModel.registerValidator(this); + } + } + + /** + * 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) { + fieldRepresentation.put(fieldname, c); + //todo we do not want at this moment to add something in ui, since ui is perharps not useable + //todo prefer to use the method installUIs + //JComponent old = fieldRepresentation.put(fieldname, c); + //setErrorRepresentation(fieldname, old, c, uiClass); + } + + public void setFieldRepresentation(Map<String, JComponent> fieldRepresentation) { + for (Map.Entry<String, JComponent> e : fieldRepresentation.entrySet()) { + setFieldRepresentation(e.getKey(), e.getValue()); + } + } + + public JComponent getFieldRepresentation(String fieldname) { + return fieldRepresentation.get(fieldname); + } + + /** + * Retourne vrai si l'objet bean a ete modifie depuis le dernier + * {@link #setBean} + * + * @return <code>true</code> if bean was modify since last {@link #setBean(Object)} invocation + */ + public boolean isChanged() { + return changed; + } + + /** + * Permet de force la remise a false de l'etat de changement du bean + * + * @param changed flag to force reset of property {@link #changed} + */ + public void setChanged(boolean changed) { + boolean oldChanged = this.changed; + this.changed = changed; + pcs.firePropertyChange("changed", oldChanged, changed); + } + + public T getBean() { + return bean; + } + + public void setBean(T bean) { + T oldBean = this.bean; + if (this.bean != null) { + try { + Method method = this.bean.getClass().getMethod("removePropertyChangeListener", PropertyChangeListener.class); + method.invoke(this.bean, l); + } catch (Exception eee) { + log.info("Can't register as listener", eee); + } + } + this.bean = bean; + if (this.bean != null) { + try { + Method method = this.bean.getClass().getMethod("addPropertyChangeListener", PropertyChangeListener.class); + method.invoke(this.bean, l); + } catch (Exception eee) { + log.info("Can't register as listener", eee); + } + } + validate(); + pcs.firePropertyChange("bean", oldBean, bean); + } + + public Class<? extends AbstractBeanValidatorUI> getUiClass() { + return uiClass; + } + + public void setUiClass(Class<? extends AbstractBeanValidatorUI> uiClass) { + this.uiClass = uiClass; + } + + /** @return <code>true</code> if errors are detected, <code>false</code> otherwise */ + public boolean hasErrors() { + //todo should also detecte actionErrors ? + return validationContext.hasFieldErrors(); + //return validationContext.hasFieldErrors() || validationContext.hasActionErrors(); + } + + public Map getFieldErrors() { + return validationContext.getFieldErrors(); + } + + public Collection getActionErrors() { + return validationContext.getActionErrors(); + } + + public Collection getActionMessages() { + return validationContext.getActionMessages(); + } + + /** install ui on required components */ + public void installUIs() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (uiClass == null) { + // use the default one + uiClass = DEFAULT_UI_CLASS; + } + for (String fieldname : fieldRepresentation.keySet()) { + try { + setErrorRepresentation(fieldname, null, fieldRepresentation.get(fieldname), uiClass); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + }); + + } + + /** + * il faut eviter le code re-intrant (durant une validation, une autre est + * demandee). Pour cela on fait la validation dans un thread, et tant + * que la premiere validation n'est pas fini, on ne repond pas aux + * solicitations. + * Cette method est public pour permettre de force une validation par + * programmation, ce qui est utile par exemple si le bean ne supporte + * pas les {@link PropertyChangeListener} + */ + public void validate() { + // on ne valide que si il y a un bean et que le resultat de la validation + // pourra etre affiche quelque part + if (bean == null || getErrorListModel() == null || fieldRepresentation.size() == 0) { + return; + } + + try { + + validationSupport.clearErrorsAndMessages(); + + getValidator().validate(bean, null, validationContext); + + if (log.isDebugEnabled()) { + log.debug("Action errors: " + validationContext.getActionErrors()); + log.debug("Action messages: " + validationContext.getActionMessages()); + log.debug("Field errors: " + validationContext.getFieldErrors()); + } + + log.info(this + " : " + validationContext.getFieldErrors()); + + // add errors + errorListModel.addErrors(this); + + for (String fieldname : fieldRepresentation.keySet()) { + JComponent c = fieldRepresentation.get(fieldname); + //todo: a supprimer mais actuellemnt le layer ne se repaint pas bien, cela n'est pas normal + c.getParent().repaint(); + } + } catch (ValidationException eee) { + log.warn("Error during validation", eee); + } + } + + protected ActionValidatorManager getValidator() { + if (validator == null) { + ConfigurationManager confManager = new ConfigurationManager(); + Configuration conf = confManager.getConfiguration(); + + ValueStackFactory vsf = conf.getContainer().getInstance( + ValueStackFactory.class); + ValueStack vs = vsf.createValueStack(); + ActionContext context = new ActionContext(vs.getContext()); + ActionContext.setContext(context); + + validator = conf.getContainer().getInstance( + ActionValidatorManager.class, "no-annotations"); + } + return validator; + } + + protected void setErrorRepresentation(String fieldname, JComponent old, JComponent c, Class<? extends AbstractBeanValidatorUI> uiClass) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException { + if (old != c) { + if (old != null) { + // suppression du jxlayer sous l'ancien composant + Container container = old.getParent(); + if (container instanceof JXLayer) { + JXLayer<?> jx = (JXLayer<?>) container; + jx.setUI(null); + } + } + if (c != null) { + // ajout du jxlayer sous ce composant + Container container = c.getParent(); + if (container instanceof JXLayer) { + Constructor<? extends AbstractBeanValidatorUI> cons = uiClass.getConstructor(String.class, String.class, ValidationAware.class); + AbstractBeanValidatorUI ui = cons.newInstance(fieldname, c.getName(), validationSupport); + //IconValidationUI ui = new IconValidationUI(fieldname, c.getName(), validationSupport); + //TranslucentValidationUI ui = new TranslucentValidationUI(fieldname, c.getName(), validationSupport); + ui.setEnabled(true); + JXLayer<JComponent> jx = (JXLayer<JComponent>) container; + jx.setUI(ui); + } + } + } + } + + protected class Listener implements PropertyChangeListener { + + public void propertyChange(PropertyChangeEvent evt) { + // only validate if the property has really changed... + Object oldValue = evt.getOldValue(); + Object newValue = evt.getNewValue(); + if (oldValue == null && newValue == null) { + return; + } + if ((oldValue != null && oldValue.equals(newValue)) || (newValue != null && newValue.equals(oldValue))) { + return; + } + if (log.isDebugEnabled()) { + log.debug("launch validation from event [name:" + evt.getPropertyName() + " <old:" + oldValue + ", new:" + newValue + ">]"); + } + validate(); + setChanged(true); + } + } + +} Copied: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidatorError.java (from rev 957, lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidatorError.java) =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidatorError.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidatorError.java 2008-10-17 20:55:15 UTC (rev 958) @@ -0,0 +1,83 @@ +package jaxx.runtime.validator; + +import org.codelutin.i18n.I18n; + +import javax.swing.JComponent; + +/** + * The model of an error. + * + * @author chemit + */ +public class BeanValidatorError<T> { + + protected BeanValidator<T> validator; + + protected String fieldName; + + protected String error; + + protected JComponent component; + + public BeanValidator<T> getValidator() { + return validator; + } + + public String getFieldName() { + return fieldName; + } + + public String getError() { + return error; + } + + public JComponent getComponent() { + return component; + } + + public void setValidator(BeanValidator<T> validator) { + this.validator = validator; + } + + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } + + public void setError(String error) { + this.error = error; + } + + public void setComponent(JComponent component) { + this.component = component; + } + + @Override + public String toString() { + return component.getName() + " : " + I18n._(error); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BeanValidatorError)) { + return false; + } + + BeanValidatorError that = (BeanValidatorError) o; + + return component.equals(that.component) && error.equals(that.error) && fieldName.equals(that.fieldName) && validator.equals(that.validator); + + } + + @Override + public int hashCode() { + int result; + result = validator.hashCode(); + result = 31 * result + fieldName.hashCode(); + result = 31 * result + error.hashCode(); + result = 31 * result + component.hashCode(); + return result; + } +} Copied: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidatorErrorListModel.java (from rev 957, lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/BeanValidatorErrorListModel.java) =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidatorErrorListModel.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidatorErrorListModel.java 2008-10-17 20:55:15 UTC (rev 958) @@ -0,0 +1,140 @@ +package jaxx.runtime.validator; + +import javax.swing.DefaultListModel; +import javax.swing.JComponent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + +/** + * The model of the list of errors + * + * @author chemit + */ +public class BeanValidatorErrorListModel extends DefaultListModel { + private static final long serialVersionUID = 1L; + + /** list of registred validators */ + protected transient List<BeanValidator<?>> validators; + + /** comporatorof errors */ + protected transient Comparator<BeanValidatorError> comparator; + + public BeanValidatorErrorListModel() { + validators = new ArrayList<BeanValidator<?>>(); + } + + public void registerValidator(BeanValidator<?> validator) { + if (validators.contains(validator)) { + throw new IllegalArgumentException("the validator " + validator + " is already registred in " + this); + } + validators.add(validator); + } + + public <T> void addErrors(BeanValidator<T> validator) { + if (!validators.contains(validator)) { + throw new IllegalArgumentException("the validator " + validator + " is not registred in " + this); + } + + // new errors for the given validator + List<BeanValidatorError> newErrors = new ArrayList<BeanValidatorError>(); + // old errors for the given validator + List<BeanValidatorError> oldErrors = new ArrayList<BeanValidatorError>(); + // old errors for other validators + List<BeanValidatorError> oldOtherErrors = new ArrayList<BeanValidatorError>(); + + // split old errors from other validators + splitOldErrors(validator, oldErrors, oldOtherErrors); + + // fill new errors + fillNewErrors(validator, newErrors); + + // check if something has changed for the given validator + if (hasChanged(oldErrors, newErrors)) { + + // reinject other errors + newErrors.addAll(oldOtherErrors); + + // sort errors + Collections.sort(newErrors, getComparator()); + + // clean errors in model + clear(); + + // reinject in list model, all the errors + for (BeanValidatorError error : newErrors) { + addElement(error); + } + + // notify thaht the model has changed + fireContentsChanged(this, 0, getSize() - 1); + } + } + + protected boolean hasChanged(List<BeanValidatorError> oldErrors, List<BeanValidatorError> newErrors) { + if (oldErrors.size() != newErrors.size()) { + return true; + } + for (BeanValidatorError oldError : oldErrors) { + if (!newErrors.contains(oldError)) { + return true; + } + } + return false; + } + + protected <T> void fillNewErrors(BeanValidator<T> validator, List<BeanValidatorError> newErrors) { + if (validator.hasErrors()) { + // inject this validator errors + for (Object o : validator.getFieldErrors().entrySet()) { + Map.Entry<?, ?> r = (Map.Entry<?, ?>) o; + BeanValidatorError<T> error; + String field = (String) r.getKey(); + + JComponent component = validator.getFieldRepresentation(field); + if (component!=null) { + for (Object errorString : (List<?>) r.getValue()) { + String error1 = (String) errorString; + error = new BeanValidatorError<T>(); + error.setComponent(component); + error.setValidator(validator); + error.setError(error1); + error.setFieldName(field); + newErrors.add(error); + } + } + } + //todo should also do actionErrors + } + } + + protected <T> void splitOldErrors(BeanValidator<T> validator, List<BeanValidatorError> oldErrors, List<BeanValidatorError> oldOtherErrors) { + Enumeration enumeration = elements(); + + while (enumeration.hasMoreElements()) { + Object o = enumeration.nextElement(); + BeanValidatorError<?> error = (BeanValidatorError) o; + if (!error.getValidator().equals(validator)) { + // error from another validator, keep it + oldOtherErrors.add(error); + } else { + oldErrors.add(error); + } + } + } + + protected Comparator<BeanValidatorError> getComparator() { + if (comparator == null) { + comparator = new Comparator<BeanValidatorError>() { + public int compare(BeanValidatorError o1, BeanValidatorError o2) { + return o1.toString().compareTo(o2.toString()); + } + }; + } + return comparator; + } + +} Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ui/AbstractBeanValidatorUI.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ui/AbstractBeanValidatorUI.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ui/AbstractBeanValidatorUI.java 2008-10-17 20:55:15 UTC (rev 958) @@ -0,0 +1,33 @@ +package jaxx.runtime.validator.ui; + +import com.opensymphony.xwork2.ValidationAware; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jdesktop.jxlayer.plaf.AbstractLayerUI; + +/** + * Abstract renderer + * + * @author chemit + */ +public abstract class AbstractBeanValidatorUI extends AbstractLayerUI<javax.swing.JComponent> { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + protected static Log log = LogFactory.getLog(AbstractBeanValidatorUI.class); + + /** bean field */ + protected String field; + + /** component id */ + protected String componentId; + + /** validation context */ + protected ValidationAware validationContext; + + public AbstractBeanValidatorUI(String field,String componentId, ValidationAware validationContext) { + this.componentId = componentId; + this.field = field; + this.validationContext = validationContext; + log.info("install " + this + "<field:" + field + ", componentId:" + componentId + ">"); + } +} Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ui/IconValidationUI.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ui/IconValidationUI.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ui/IconValidationUI.java 2008-10-17 20:55:15 UTC (rev 958) @@ -0,0 +1,61 @@ +package jaxx.runtime.validator.ui; + +import org.jdesktop.jxlayer.JXLayer; + +import javax.swing.JComponent; +import javax.swing.BorderFactory; +import java.awt.image.BufferedImage; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Color; + +import com.opensymphony.xwork2.ValidationAware; + +/** @author chemit */ +public class IconValidationUI extends AbstractBeanValidatorUI { + + // The red icon to be shown at the layer's corner + private final static BufferedImage INVALID_ICON; + + static { + int width = 7; + int height = 8; + INVALID_ICON = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2 = (Graphics2D) INVALID_ICON.getGraphics(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + g2.setColor(Color.RED); + g2.fillRect(0, 0, width, height); + g2.setColor(Color.WHITE); + g2.drawLine(0, 0, width, height); + g2.drawLine(0, height, width, 0); + g2.dispose(); + } + + public IconValidationUI(String field, String componentId, ValidationAware validationContext) { + super(field,componentId,validationContext); + } + + @Override + public void installUI(JComponent c) { + super.installUI(c); + c.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 3)); + } + + @Override + public void uninstallUI(JComponent c) { + super.uninstallUI(c); + c.setBorder(null); + } + + @Override + protected void paintLayer(Graphics2D g2, JXLayer<JComponent> l) { + super.paintLayer(g2, l); + //log.info("paintLayer "+field); + // There is no need to take insets into account for this painter + if (validationContext.getFieldErrors().containsKey(field)) { + g2.drawImage(INVALID_ICON, l.getWidth() - INVALID_ICON.getWidth() - 1, 0, null); + // g2.drawImage(INVALID_ICON, 0, 0, null); + } + } +} Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ui/TranslucentValidationUI.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ui/TranslucentValidationUI.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ui/TranslucentValidationUI.java 2008-10-17 20:55:15 UTC (rev 958) @@ -0,0 +1,42 @@ +package jaxx.runtime.validator.ui; + +import com.opensymphony.xwork2.ValidationAware; +import org.jdesktop.jxlayer.JXLayer; + +import javax.swing.JComponent; +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Rectangle; + +/** @author chemit */ +public class TranslucentValidationUI extends AbstractBeanValidatorUI { + + public TranslucentValidationUI(String field, String componentId, ValidationAware validationContext) { + super(componentId, field, validationContext); + + } + + @Override + protected void paintLayer(Graphics2D g2, JXLayer<JComponent> l) { + // paints the layer as is + super.paintLayer(g2, l); + + // to be in sync with the view if the layer has a border + Insets layerInsets = l.getInsets(); + g2.translate(layerInsets.left, layerInsets.top); + + JComponent view = l.getView(); + // To prevent painting on view's border + Insets insets = view.getInsets(); + g2.clip(new Rectangle(insets.left, insets.top, + view.getWidth() - insets.left - insets.right, + view.getHeight() - insets.top - insets.bottom)); + + g2.setColor(!validationContext.getFieldErrors().containsKey(field) ? + Color.GREEN : Color.RED); + g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .2f)); + g2.fillRect(0, 0, l.getWidth(), l.getHeight()); + } +} Deleted: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/BeanValidatorHandler.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/BeanValidatorHandler.java 2008-10-17 19:44:04 UTC (rev 957) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/BeanValidatorHandler.java 2008-10-17 20:55:15 UTC (rev 958) @@ -1,282 +0,0 @@ -/* - * Copyright 2006 Ethan Nicholas. All rights reserved. - * Use is subject to license terms. - */ -package jaxx.tags.swing; - -import jaxx.CompilerException; -import jaxx.compiler.CompiledObject; -import jaxx.compiler.JAXXCompiler; -import jaxx.introspection.JAXXBeanInfo; -import jaxx.introspection.JAXXPropertyDescriptor; -import jaxx.reflect.ClassDescriptor; -import jaxx.reflect.ClassDescriptorLoader; -import jaxx.runtime.BeanValidator; -import jaxx.tags.DefaultObjectHandler; -import jaxx.types.TypeManager; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.w3c.dom.Element; - -import java.beans.IntrospectionException; -import java.io.IOException; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeMap; - -public class BeanValidatorHandler extends DefaultObjectHandler { - - public static final String BEAN_VALIDATOR_TAG = BeanValidator.class.getSimpleName(); - public static final String BEAN_ATTRIBUTE = "bean"; - public static final String ERROR_LIST_MODEL_ATTRIBUTE = "errorListModel"; - public static final String AUTOFIELD_ATTRIBUTE = "autoField"; - public static final String STRICT_MODE_ATTRIBUTE = "strictMode"; - - /** to use log facility, just put in your code: log.info(\"...\"); */ - static Log log = LogFactory.getLog(BeanValidatorHandler.class); - - /** - * Creates a new <code>DefaultObjectHandler</code> which provides support for the specified class. The - * class is not actually introspected until the {@link #compileFirstPass} method is invoked. - * - * @param beanClass the class which this handler supports - */ - public BeanValidatorHandler(ClassDescriptor beanClass) { - super(beanClass); - ClassDescriptorLoader.checkSupportClass(getClass(), beanClass, BeanValidator.class); - } - - @Override - protected CompiledObject createCompiledObject(String id, JAXXCompiler compiler) { - return new CompiledBeanValidator(id, getBeanClass(), compiler); - } - - @Override - protected void compileChildTagFirstPass(Element tag, JAXXCompiler compiler) throws CompilerException, IOException { - if (compiler.getOptions().isVerbose()) { - log.info(tag); - } - if (!tag.getLocalName().equals(FieldValidatorHandler.FIELD_VALIDATOR_TAG)) { - compiler.reportError("tag '" + tag.getParentNode().getLocalName() + "' may only contain " + FieldValidatorHandler.FIELD_VALIDATOR_TAG + " as children, but found : " + tag.getLocalName()); - } else { - compiler.compileFirstPass(tag); - } - } - - @Override - public void compileSecondPass(Element tag, JAXXCompiler compiler) throws CompilerException, IOException { - - super.compileSecondPass(tag, compiler); - - CompiledBeanValidator info = (CompiledBeanValidator) objectMap.get(tag); - - String tmp = info.getErrorListModel(); - - if (tmp != null) { - if (compiler.checkReference(tag, tmp, true, ERROR_LIST_MODEL_ATTRIBUTE)) { - String code = getSetPropertyCode(info.getJavaCode(), ERROR_LIST_MODEL_ATTRIBUTE, tmp, compiler); - info.appendAdditionCode(code); - } - } - String bean = info.getBean(); - if (bean != null) { - if (compiler.checkReference(tag, bean, true, BEAN_ATTRIBUTE)) { - String code = getSetPropertyCode(info.getJavaCode(), BEAN_ATTRIBUTE, bean, compiler); - info.appendAdditionCode(code); - } - } - - if (info.getAutoField()) { - if (bean == null) { - compiler.reportError("tag '" + tag.getLocalName() + "' need a " + BEAN_ATTRIBUTE + " attribute to use autofield mode"); - } else { - registerAutoFieldBean(tag, compiler, info); - } - } - - // add fieldrepresentation invocations - addFieldRepresentations(tag, compiler, info); - - // register the validator in compiler - compiler.registerValidator(info); - - // close the compiled object - compiler.closeComponent(info); - } - - @Override - protected void setDefaults(CompiledObject object, Element tag, JAXXCompiler compiler) { - // open the compiled object - compiler.openInvisibleComponent(object); - } - - @Override - public void setAttribute(CompiledObject object, String propertyName, String stringValue, boolean inline, JAXXCompiler compiler) { - if (compiler.getOptions().isVerbose()) { - log.info(propertyName + " : " + stringValue + " for " + object); - } - object.addProperty(propertyName, stringValue); - } - - protected void registerAutoFieldBean(Element tag, JAXXCompiler compiler, CompiledBeanValidator info) { - JAXXBeanInfo beanInfo = info.getBeanDescriptor(compiler); - for (JAXXPropertyDescriptor beanProperty : beanInfo.getJAXXPropertyDescriptors()) { - String descriptionName = beanProperty.getName(); - if (compiler.getOptions().isVerbose()) { - log.info("try to bind on bean " + beanInfo.getJAXXBeanDescriptor().getName() + " property " + descriptionName); - } - if (beanProperty.getWriteMethodDescriptor() == null) { - // read-only property - continue; - } - if (info.getFields().containsKey(descriptionName)) { - // already defined in field - continue; - } - if (!compiler.checkReference(tag, descriptionName, info.getStrictMode(), null)) { - // no editor component found - continue; - } - // ok add the field mapping - info.addField(descriptionName, descriptionName, compiler); - } - } - - protected void addFieldRepresentations(Element tag, JAXXCompiler compiler, CompiledBeanValidator info) { - for (Entry<String, String> entry : info.getFields().entrySet()) { - String propertyName = entry.getKey(); - String component = entry.getValue(); - if (!checkBeanProperty(compiler, info, propertyName)) { - // property not find on bean - continue; - } - if (!compiler.checkReference(tag, component, true, null)) { - // editor component not find on ui - continue; - } - String keyCode = TypeManager.getJavaCode(propertyName); - info.appendAdditionCode(info.getJavaCode() + ".setFieldRepresentation(" + keyCode + ", " + component + ");"); - - } - } - - protected boolean checkBeanProperty(JAXXCompiler compiler, CompiledBeanValidator info, String propertyName) { - - for (JAXXPropertyDescriptor beanProperty : info.getBeanDescriptor(compiler).getJAXXPropertyDescriptors()) { - if (beanProperty.getName().equals(propertyName)) { - if (beanProperty.getWriteMethodDescriptor() == null) { - // read-onlyproperty - compiler.reportError("could not bind the readonly property '" + propertyName + "' on bean [" + info.getBean() + "] "); - return false; - } - return true; - } - } - compiler.reportError("could not find the property '" + propertyName + "' on bean [" + info.getBean() + "] "); - return false; - } - - /** @author chemit */ - public static class CompiledBeanValidator extends CompiledObject { - - protected Map<String, String> fields; - protected String bean; - protected String errorListModel; - protected Boolean autoField; - protected Boolean strictMode; - protected JAXXBeanInfo beanDescriptor; - - public CompiledBeanValidator(String id, ClassDescriptor objectClass, JAXXCompiler compiler) { - super(id, objectClass, compiler); - fields = new TreeMap<String, String>(); - } - - public Map<String, String> getFields() { - return fields; - } - - public void setFields(Map<String, String> fields) { - this.fields = fields; - } - - @Override - public void addProperty(String property, String value) { - - if (BEAN_ATTRIBUTE.equals(property)) { - if (value != null && !value.trim().isEmpty()) { - bean = value; - } - return; - } - if (ERROR_LIST_MODEL_ATTRIBUTE.equals(property)) { - if (value != null && !value.trim().isEmpty()) { - errorListModel = value; - } - return; - } - if (AUTOFIELD_ATTRIBUTE.equals(property)) { - if (value != null && !value.trim().isEmpty()) { - - autoField = (Boolean) TypeManager.convertFromString(value, Boolean.class); - } - return; - } - if (STRICT_MODE_ATTRIBUTE.equals(property)) { - if (value != null && !value.trim().isEmpty()) { - - strictMode = (Boolean) TypeManager.convertFromString(value, Boolean.class); - } - return; - } - //todo should not allowed to find other attributes - super.addProperty(property, value); - } - - public void addField(String id, String component, JAXXCompiler compiler) { - if (fields.containsKey(id)) { - compiler.reportError("duplicate field '" + id + "' for validator " + this); - } else { - if (compiler.getOptions().isVerbose()) { - log.info("add field <" + id + ":" + component + ">"); - } - fields.put(id, component); - } - } - - public String getBean() { - return bean; - } - - public String getErrorListModel() { - return errorListModel; - } - - public boolean getAutoField() { - return autoField != null && autoField; - } - - public boolean getStrictMode() { - return strictMode != null && strictMode; - } - - public JAXXBeanInfo getBeanDescriptor(JAXXCompiler compiler) { - if (beanDescriptor == null && bean != null) { - try { - ClassDescriptor beanClassDescriptor = ClassDescriptorLoader.getClassDescriptor(compiler.getSymbolTable().getClassTagIds().get(bean)); - beanDescriptor = getJAXXBeanInfo(beanClassDescriptor); - } catch (ClassNotFoundException e) { - compiler.reportError("could not load class " + bean); - } catch (IntrospectionException e) { - compiler.reportError("could not load class " + bean); - } - } - return beanDescriptor; - } - - @Override - public void addChild(CompiledObject child, String constraints, JAXXCompiler compiler) throws CompilerException { - // do nothing - compiler.reportError("can not add CompiledObject in the tag '" + BEAN_VALIDATOR_TAG + " (only field tags)"); - } - } -} \ No newline at end of file Deleted: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/FieldValidatorHandler.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/FieldValidatorHandler.java 2008-10-17 19:44:04 UTC (rev 957) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/FieldValidatorHandler.java 2008-10-17 20:55:15 UTC (rev 958) @@ -1,73 +0,0 @@ -/* - * Copyright 2006 Ethan Nicholas. All rights reserved. - * Use is subject to license terms. - */ -package jaxx.tags.swing; - -import jaxx.CompilerException; -import jaxx.compiler.JAXXCompiler; -import jaxx.reflect.ClassDescriptorLoader; -import jaxx.runtime.BeanValidator; -import jaxx.tags.TagHandler; -import jaxx.tags.swing.BeanValidatorHandler.CompiledBeanValidator; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.w3c.dom.Element; - -import java.io.IOException; - -public class FieldValidatorHandler implements TagHandler { - - public static final String FIELD_VALIDATOR_TAG = "field"; - public static final String NAME_ATTRIBUTE = "name"; - public static final String COMPONENT_ATTRIBUTE = "component"; - - /** to use log facility, just put in your code: log.info(\"...\"); */ - static private Log log = LogFactory.getLog(FieldValidatorHandler.class); - - public void compileFirstPass(Element tag, JAXXCompiler compiler) throws CompilerException, IOException { - if (compiler.getOptions().isVerbose()) { - log.info(tag); - } - //todo check there is no child - } - - public void compileSecondPass(Element tag, JAXXCompiler compiler) throws CompilerException, IOException { - if (compiler.getOptions().isVerbose()) { - log.info(tag); - } - - if (!ClassDescriptorLoader.getClassDescriptor(BeanValidator.class).isAssignableFrom(compiler.getOpenComponent().getObjectClass())) { - compiler.reportError(FIELD_VALIDATOR_TAG + " tag may only appear within " + BeanValidatorHandler.BEAN_VALIDATOR_TAG + " tag but was " + tag); - return; - } - - CompiledBeanValidator validator = (CompiledBeanValidator) compiler.getOpenComponent(); - - String name = tag.getAttribute(NAME_ATTRIBUTE); - String component = tag.getAttribute(COMPONENT_ATTRIBUTE); - if (name == null || name.trim().isEmpty()) { - compiler.reportError(FIELD_VALIDATOR_TAG + " tag requires a " + NAME_ATTRIBUTE + " attribute"); - return; - } - name = name.trim(); - if (component == null || component.trim().isEmpty()) { - // try to use the name as component - if (!compiler.checkReference(tag, name, false, name)) { - compiler.reportError(FIELD_VALIDATOR_TAG + " tag requires a " + COMPONENT_ATTRIBUTE + " attribute, try to use the name attribute ["+name+"] for the component, but no such component found"); - return; - } - component = name; - } - component = component.trim(); - - // check component exist (again perharps, but if error will known exactly which tag failed...) - if (compiler.checkReference(tag, component, true, component)) { - // add a field - validator.addField(name, component, compiler); - } - - - } - -} \ No newline at end of file Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/SwingInitializer.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/SwingInitializer.java 2008-10-17 19:44:04 UTC (rev 957) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/SwingInitializer.java 2008-10-17 20:55:15 UTC (rev 958) @@ -13,13 +13,13 @@ import jaxx.runtime.swing.JAXXTab; import jaxx.runtime.swing.JAXXTree; import jaxx.runtime.swing.Table; -import jaxx.runtime.BeanValidator; +import jaxx.runtime.validator.BeanValidator; import jaxx.spi.Initializer; import jaxx.tags.DefaultComponentHandler; import jaxx.tags.DefaultObjectHandler; import jaxx.tags.TagManager; -import jaxx.tags.swing.BeanValidatorHandler; -import jaxx.tags.swing.FieldValidatorHandler; +import jaxx.tags.validator.BeanValidatorHandler; +import jaxx.tags.validator.FieldValidatorHandler; import jaxx.types.KeyStrokeConverter; import jaxx.types.TypeManager; Copied: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/BeanValidatorHandler.java (from rev 953, lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/BeanValidatorHandler.java) =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/BeanValidatorHandler.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/BeanValidatorHandler.java 2008-10-17 20:55:15 UTC (rev 958) @@ -0,0 +1,312 @@ +/* + * Copyright 2006 Ethan Nicholas. All rights reserved. + * Use is subject to license terms. + */ +package jaxx.tags.validator; + +import jaxx.CompilerException; +import jaxx.compiler.CompiledObject; +import jaxx.compiler.JAXXCompiler; +import jaxx.introspection.JAXXBeanInfo; +import jaxx.introspection.JAXXPropertyDescriptor; +import jaxx.reflect.ClassDescriptor; +import jaxx.reflect.ClassDescriptorLoader; +import jaxx.runtime.validator.BeanValidator; +import jaxx.runtime.validator.ui.AbstractBeanValidatorUI; +import jaxx.tags.DefaultObjectHandler; +import jaxx.types.TypeManager; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Element; + +import java.beans.IntrospectionException; +import java.io.IOException; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; + +public class BeanValidatorHandler extends DefaultObjectHandler { + + public static final String BEAN_VALIDATOR_TAG = BeanValidator.class.getSimpleName(); + public static final String BEAN_ATTRIBUTE = "bean"; + public static final String ERROR_LIST_MODEL_ATTRIBUTE = "errorListModel"; + public static final String AUTOFIELD_ATTRIBUTE = "autoField"; + public static final String UI_CLASS_ATTRIBUTE = "uiClass"; + public static final String STRICT_MODE_ATTRIBUTE = "strictMode"; + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static Log log = LogFactory.getLog(BeanValidatorHandler.class); + + /** + * Creates a new <code>DefaultObjectHandler</code> which provides support for the specified class. The + * class is not actually introspected until the {@link #compileFirstPass} method is invoked. + * + * @param beanClass the class which this handler supports + */ + public BeanValidatorHandler(ClassDescriptor beanClass) { + super(beanClass); + ClassDescriptorLoader.checkSupportClass(getClass(), beanClass, BeanValidator.class); + } + + @Override + protected CompiledObject createCompiledObject(String id, JAXXCompiler compiler) { + return new CompiledBeanValidator(id, getBeanClass(), compiler); + } + + @Override + protected void compileChildTagFirstPass(Element tag, JAXXCompiler compiler) throws CompilerException, IOException { + if (compiler.getOptions().isVerbose()) { + log.info(tag); + } + if (!tag.getLocalName().equals(FieldValidatorHandler.FIELD_VALIDATOR_TAG)) { + compiler.reportError("tag '" + tag.getParentNode().getLocalName() + "' may only contain " + FieldValidatorHandler.FIELD_VALIDATOR_TAG + " as children, but found : " + tag.getLocalName()); + } else { + compiler.compileFirstPass(tag); + } + } + + @Override + public void compileSecondPass(Element tag, JAXXCompiler compiler) throws CompilerException, IOException { + + super.compileSecondPass(tag, compiler); + + CompiledBeanValidator info = (CompiledBeanValidator) objectMap.get(tag); + + String tmp; + + tmp = info.getErrorListModel(); + if (tmp != null) { + if (compiler.checkReference(tag, tmp, true, ERROR_LIST_MODEL_ATTRIBUTE)) { + String code = getSetPropertyCode(info.getJavaCode(), ERROR_LIST_MODEL_ATTRIBUTE, tmp, compiler); + info.appendAdditionCode(code); + } + } + + tmp = info.getUiClass(); + if (tmp != null) { + try { + ClassDescriptor uiClass = ClassDescriptorLoader.getClassDescriptor(tmp); + if (!ClassDescriptorLoader.getClassDescriptor(AbstractBeanValidatorUI.class).isAssignableFrom(uiClass)) { + compiler.reportError("attribute 'ui' :'" + tmp + "' is not assignable from class " + AbstractBeanValidatorUI.class); + } else { + String code = getSetPropertyCode(info.getJavaCode(), UI_CLASS_ATTRIBUTE, uiClass.getName() + ".class", compiler); + info.appendAdditionCode(code); + } + } catch (ClassNotFoundException e) { + compiler.reportError("class not found '" + tmp + "'"); + } + } + + String bean = info.getBean(); + if (bean != null) { + if (compiler.checkReference(tag, bean, true, BEAN_ATTRIBUTE)) { + String code = getSetPropertyCode(info.getJavaCode(), BEAN_ATTRIBUTE, bean, compiler); + info.appendAdditionCode(code); + } + } + + if (info.getAutoField()) { + if (bean == null) { + compiler.reportError("tag '" + tag.getLocalName() + "' need a " + BEAN_ATTRIBUTE + " attribute to use autofield mode"); + } else { + registerAutoFieldBean(tag, compiler, info); + } + } + + // add fieldrepresentation invocations + addFieldRepresentations(tag, compiler, info); + + // register the validator in compiler + compiler.registerValidator(info); + + // close the compiled object + compiler.closeComponent(info); + } + + @Override + protected void setDefaults(CompiledObject object, Element tag, JAXXCompiler compiler) { + // open the compiled object + compiler.openInvisibleComponent(object); + } + + @Override + public void setAttribute(CompiledObject object, String propertyName, String stringValue, boolean inline, JAXXCompiler compiler) { + if (compiler.getOptions().isVerbose()) { + log.info(propertyName + " : " + stringValue + " for " + object); + } + object.addProperty(propertyName, stringValue); + } + + protected void registerAutoFieldBean(Element tag, JAXXCompiler compiler, CompiledBeanValidator info) { + JAXXBeanInfo beanInfo = info.getBeanDescriptor(compiler); + for (JAXXPropertyDescriptor beanProperty : beanInfo.getJAXXPropertyDescriptors()) { + String descriptionName = beanProperty.getName(); + if (compiler.getOptions().isVerbose()) { + log.info("try to bind on bean " + beanInfo.getJAXXBeanDescriptor().getName() + " property " + descriptionName); + } + if (beanProperty.getWriteMethodDescriptor() == null) { + // read-only property + continue; + } + if (info.getFields().containsKey(descriptionName)) { + // already defined in field + continue; + } + if (!compiler.checkReference(tag, descriptionName, info.getStrictMode(), null)) { + // no editor component found + continue; + } + // ok add the field mapping + info.addField(descriptionName, descriptionName, compiler); + } + } + + protected void addFieldRepresentations(Element tag, JAXXCompiler compiler, CompiledBeanValidator info) { + for (Entry<String, String> entry : info.getFields().entrySet()) { + String propertyName = entry.getKey(); + String component = entry.getValue(); + if (!checkBeanProperty(compiler, info, propertyName)) { + // property not find on bean + continue; + } + if (!compiler.checkReference(tag, component, true, null)) { + // editor component not find on ui + continue; + } + String keyCode = TypeManager.getJavaCode(propertyName); + info.appendAdditionCode(info.getJavaCode() + ".setFieldRepresentation(" + keyCode + ", " + component + ");"); + + } + } + + protected boolean checkBeanProperty(JAXXCompiler compiler, CompiledBeanValidator info, String propertyName) { + + for (JAXXPropertyDescriptor beanProperty : info.getBeanDescriptor(compiler).getJAXXPropertyDescriptors()) { + if (beanProperty.getName().equals(propertyName)) { + if (beanProperty.getWriteMethodDescriptor() == null) { + // read-onlyproperty + compiler.reportError("could not bind the readonly property '" + propertyName + "' on bean [" + info.getBean() + "] "); + return false; + } + return true; + } + } + compiler.reportError("could not find the property '" + propertyName + "' on bean [" + info.getBean() + "] "); + return false; + } + + /** @author chemit */ + public static class CompiledBeanValidator extends CompiledObject { + + protected Map<String, String> fields; + protected String bean; + protected String uiClass; + protected String errorListModel; + protected Boolean autoField; + protected Boolean strictMode; + protected JAXXBeanInfo beanDescriptor; + + public CompiledBeanValidator(String id, ClassDescriptor objectClass, JAXXCompiler compiler) { + super(id, objectClass, compiler); + fields = new TreeMap<String, String>(); + } + + public Map<String, String> getFields() { + return fields; + } + + public void setFields(Map<String, String> fields) { + this.fields = fields; + } + + @Override + public void addProperty(String property, String value) { + + if (BEAN_ATTRIBUTE.equals(property)) { + if (value != null && !value.trim().isEmpty()) { + bean = value; + } + return; + } + if (ERROR_LIST_MODEL_ATTRIBUTE.equals(property)) { + if (value != null && !value.trim().isEmpty()) { + errorListModel = value; + } + return; + } + if (UI_CLASS_ATTRIBUTE.equals(property)) { + if (value != null && !value.trim().isEmpty()) { + uiClass = value; + } + return; + } + if (AUTOFIELD_ATTRIBUTE.equals(property)) { + if (value != null && !value.trim().isEmpty()) { + + autoField = (Boolean) TypeManager.convertFromString(value, Boolean.class); + } + return; + } + if (STRICT_MODE_ATTRIBUTE.equals(property)) { + if (value != null && !value.trim().isEmpty()) { + + strictMode = (Boolean) TypeManager.convertFromString(value, Boolean.class); + } + return; + } + //todo should not allowed to find other attributes + super.addProperty(property, value); + } + + public void addField(String id, String component, JAXXCompiler compiler) { + if (fields.containsKey(id)) { + compiler.reportError("duplicate field '" + id + "' for validator " + this); + } else { + if (compiler.getOptions().isVerbose()) { + log.info("add field <" + id + ":" + component + ">"); + } + fields.put(id, component); + } + } + + public String getBean() { + return bean; + } + + public String getErrorListModel() { + return errorListModel; + } + + public boolean getAutoField() { + return autoField != null && autoField; + } + + public boolean getStrictMode() { + return strictMode != null && strictMode; + } + + public String getUiClass() { + return uiClass; + } + + public JAXXBeanInfo getBeanDescriptor(JAXXCompiler compiler) { + if (beanDescriptor == null && bean != null) { + try { + ClassDescriptor beanClassDescriptor = ClassDescriptorLoader.getClassDescriptor(compiler.getSymbolTable().getClassTagIds().get(bean)); + beanDescriptor = getJAXXBeanInfo(beanClassDescriptor); + } catch (ClassNotFoundException e) { + compiler.reportError("could not load class " + bean); + } catch (IntrospectionException e) { + compiler.reportError("could not load class " + bean); + } + } + return beanDescriptor; + } + + @Override + public void addChild(CompiledObject child, String constraints, JAXXCompiler compiler) throws CompilerException { + // do nothing + compiler.reportError("can not add CompiledObject in the tag '" + BEAN_VALIDATOR_TAG + " (only field tags)"); + } + } +} \ No newline at end of file Property changes on: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/BeanValidatorHandler.java ___________________________________________________________________ Name: svn:mergeinfo + Copied: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/FieldValidatorHandler.java (from rev 948, lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/swing/FieldValidatorHandler.java) =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/FieldValidatorHandler.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/FieldValidatorHandler.java 2008-10-17 20:55:15 UTC (rev 958) @@ -0,0 +1,73 @@ +/* + * Copyright 2006 Ethan Nicholas. All rights reserved. + * Use is subject to license terms. + */ +package jaxx.tags.validator; + +import jaxx.CompilerException; +import jaxx.compiler.JAXXCompiler; +import jaxx.reflect.ClassDescriptorLoader; +import jaxx.runtime.validator.BeanValidator; +import jaxx.tags.TagHandler; +import jaxx.tags.validator.BeanValidatorHandler.CompiledBeanValidator; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Element; + +import java.io.IOException; + +public class FieldValidatorHandler implements TagHandler { + + public static final String FIELD_VALIDATOR_TAG = "field"; + public static final String NAME_ATTRIBUTE = "name"; + public static final String COMPONENT_ATTRIBUTE = "component"; + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(FieldValidatorHandler.class); + + public void compileFirstPass(Element tag, JAXXCompiler compiler) throws CompilerException, IOException { + if (compiler.getOptions().isVerbose()) { + log.info(tag); + } + //todo check there is no child + } + + public void compileSecondPass(Element tag, JAXXCompiler compiler) throws CompilerException, IOException { + if (compiler.getOptions().isVerbose()) { + log.info(tag); + } + + if (!ClassDescriptorLoader.getClassDescriptor(BeanValidator.class).isAssignableFrom(compiler.getOpenComponent().getObjectClass())) { + compiler.reportError(FIELD_VALIDATOR_TAG + " tag may only appear within " + BeanValidatorHandler.BEAN_VALIDATOR_TAG + " tag but was " + tag); + return; + } + + CompiledBeanValidator validator = (CompiledBeanValidator) compiler.getOpenComponent(); + + String name = tag.getAttribute(NAME_ATTRIBUTE); + String component = tag.getAttribute(COMPONENT_ATTRIBUTE); + if (name == null || name.trim().isEmpty()) { + compiler.reportError(FIELD_VALIDATOR_TAG + " tag requires a " + NAME_ATTRIBUTE + " attribute"); + return; + } + name = name.trim(); + if (component == null || component.trim().isEmpty()) { + // try to use the name as component + if (!compiler.checkReference(tag, name, false, name)) { + compiler.reportError(FIELD_VALIDATOR_TAG + " tag requires a " + COMPONENT_ATTRIBUTE + " attribute, try to use the name attribute ["+name+"] for the component, but no such component found"); + return; + } + component = name; + } + component = component.trim(); + + // check component exist (again perharps, but if error will known exactly which tag failed...) + if (compiler.checkReference(tag, component, true, component)) { + // add a field + validator.addField(name, component, compiler); + } + + + } + +} \ No newline at end of file