Author: tchemit Date: 2011-01-24 14:08:51 +0100 (Mon, 24 Jan 2011) New Revision: 2040 Url: http://nuiton.org/repositories/revision/nuiton-utils/2040 Log: strict check on fields when loading model of validator + finish reimplementation of BeanValidator (and add methods to obtain them in NuitonValidatorFactory) + remove legacy api Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorFactory.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorUtil.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/package-info.java trunk/nuiton-validator/src/test/resources/org/nuiton/validator/bean/SimpleBean-marchepo-error-validation.xml Removed: trunk/nuiton-validator/src/main/java/org/nuiton/validator/legacy/ trunk/nuiton-validator/src/test/java/org/nuiton/validator/legacy/ Modified: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidator.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorListener.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/xwork2/XWork2ValidatorUtil.java trunk/nuiton-validator/src/test/java/org/nuiton/validator/AbstractValidatorDetectorTest.java trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/BeanValidatorTest.java trunk/nuiton-validator/src/test/java/org/nuiton/validator/model/ModelValidatorDetectorTestImpl.java trunk/nuiton-validator/src/test/resources/org/nuiton/validator/bean/SimpleBean-error-validation.xml Modified: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidator.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidator.java 2011-01-24 12:54:50 UTC (rev 2039) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidator.java 2011-01-24 13:08:51 UTC (rev 2040) @@ -31,11 +31,15 @@ * A such validator is designed to validate to keep the validation of a bean, * means the bean is attached to the validator (field {@link #bean}. * <p/> - * <b>Note:</b> The {@code BeanValidator} should never be used for + * A such validator is also a JavaBean and you can listen his states + * modifications via the classic java bean api. + * <p/> + * <strong>Note:</strong> The {@code BeanValidator} should never be used for * validation in a service approch since it needs to keep a reference to the * bean to validate. * * @author tchemit <chemit@codelutin.com> + * @see BeanValidatorListener * @since 2.0 */ public class BeanValidator<O> { @@ -52,12 +56,18 @@ /** * Name of the bounded property {@code context}. * - * @see #bean * @see #getContext() * @see #setContext(String) */ public static final String CONTEXT_PROPERTY = "context"; + /** + * Name of the bounded property {@code scopes}. + * + * @see #getScopes() + * @see #setScopes(NuitonValidatorScope...) + */ + public static final String SCOPES_PROPERTY = "scopes"; /** * Name of the bounded property {@link #valid}. @@ -83,10 +93,16 @@ /** The bean to validate. */ protected O bean; + /** To chain to another validator (acting as parent of this one). */ + protected BeanValidator<?> parentValidator; + /** The delegate validator used to validate the bean. */ protected NuitonValidator<O> delegate; - /** State of validation */ + /** + * State of validation (keep all messages of validation for the filled + * bean). + */ protected NuitonValidatorResult messages; /** @@ -95,7 +111,7 @@ */ protected boolean changed; - /** state of the validator (is true if no errors of error scope is found) */ + /** State of the validator (is true if no errors of error scope is found). */ protected boolean valid = true; /** @@ -108,7 +124,7 @@ /** map of conversion errors detected by this validator */ protected Map<String, String> conversionErrors; - /** listener that listens on bean modification */ + /** Listener that listens on bean modification. */ protected PropertyChangeListener l; /** delegate property change support */ @@ -117,15 +133,19 @@ /** A list of event listeners for this validators */ protected EventListenerList listenerList = new EventListenerList(); + /** + * The provider of delegate validators. + * <p/> + * It will also produce validator model. + * + * @see NuitonValidatorProvider + */ protected final NuitonValidatorProvider validatorProvider; - protected final NuitonValidatorModel<O> initialValidatorModel; - - protected NuitonValidatorModel<O> validatorModel; - public BeanValidator(NuitonValidatorProvider validatorProvider, Class<O> beanClass, String context) { + this(validatorProvider, beanClass, context, NuitonValidatorScope.values() @@ -135,50 +155,69 @@ public BeanValidator(NuitonValidatorProvider validatorProvider, Class<O> beanClass, String context, - NuitonValidatorScope... filterScopes) { + NuitonValidatorScope... scopes) { + + // check if given bean class is Javabean compiliant + boolean javaBeanCompiliant = BeanUtil.isJavaBeanCompiliant(beanClass); + + if (!javaBeanCompiliant) { + + throw new IllegalArgumentException( + beanClass.getName() + " is not JavaBean compiliant (" + + BeanUtil.ADD_PROPERTY_CHANGE_LISTENER + ", or " + + BeanUtil.REMOVE_PROPERTY_CHANGE_LISTENER + + " method not found)."); + } + this.validatorProvider = validatorProvider; pcs = new PropertyChangeSupport(this); conversionErrors = new TreeMap<String, String>(); - // initial model of validation (keep it when need to change - // the validator model : we have all we need inside this one (type, - // scopes...)) - initialValidatorModel = validatorProvider.getModel(beanClass, - context, - filterScopes + // build delegate validator + rebuildDelegateValidator( + beanClass, + context, + scopes ); - // at the begin, validator model is exactly the initial validator model - validatorModel = initialValidatorModel; + // context has changed + firePropertyChange(CONTEXT_PROPERTY, + null, + context + ); - pcs.firePropertyChange(CONTEXT_PROPERTY, - null, - context + // scopes has changed + firePropertyChange(SCOPES_PROPERTY, + null, + scopes ); l = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { - // chaque modification lance la validation + + // the bean has changed, replay validation doValidate(); } }; } /** - * Retourne vrai si l'objet bean a ete modifie depuis le dernier {@link - * #setBean} + * Obtain the {@link #changed} property value. + * <p/> + * Returns {@code true} if bean was modified since last + * time a bean was attached. * - * @return <code>true</code> if bean was modify since last {@link - * #setBean(Object)} invocation + * @return {@code true} if bean was modified since last attachement of + * a bean. */ public boolean isChanged() { return changed; } /** - * Permet de force la remise a false de l'etat de changement du bean + * To force the value of the property {@link #changed}. * * @param changed flag to force reset of property {@link #changed} */ @@ -186,10 +225,9 @@ this.changed = changed; // force the property to be fired (never pass the older value) - pcs.firePropertyChange(CHANGED_PROPERTY, null, changed); + firePropertyChange(CHANGED_PROPERTY, null, changed); } - public boolean isCanValidate() { return canValidate; } @@ -198,21 +236,45 @@ this.canValidate = canValidate; } + /** + * Obtain the {@link #valid} property value. + * + * @return {@code true} if attached bean is valid (no error or fatal messages) + */ public boolean isValid() { return valid; } + /** + * Change the value of the {@link #valid} property. + * + * @param valid the new value of the property + */ public void setValid(boolean valid) { this.valid = valid; // force the property to be fired (never pass the older value) - pcs.firePropertyChange(VALID_PROPERTY, null, valid); + firePropertyChange(VALID_PROPERTY, null, valid); } + /** + * Obtain the actual bean attached to the validator. + * + * @return the bean attached to the validor or {@code null} if no bean + * is attached + */ public O getBean() { return bean; } + /** + * Change the attached bean. + * <p/> + * As a side effect, the internal {@link #messages} will be reset. + * + * @param bean the bean to attach (can be {@code null} to reset the + * validator). + */ public void setBean(O bean) { O oldBean = this.bean; if (log.isDebugEnabled()) { @@ -256,38 +318,136 @@ } setChanged(false); setValid(!messages.hasFatalMessages() && !messages.hasErrorMessagess()); - pcs.firePropertyChange(BEAN_PROPERTY, oldBean, bean); + firePropertyChange(BEAN_PROPERTY, oldBean, bean); } public String getContext() { - return validatorModel == null ? null : validatorModel.getContext(); + return delegate.getModel().getContext(); } public void setContext(String context) { String oldContext = getContext(); + NuitonValidatorModel<O> validatorModel = delegate.getModel(); + // compute the new validator model - NuitonValidatorScope[] scopes = new NuitonValidatorScope[initialValidatorModel.getScopes().size()]; - validatorModel = validatorProvider.getModel(initialValidatorModel.getType(), context, scopes); + NuitonValidatorScope[] scopes = new NuitonValidatorScope[validatorModel.getScopes().size()]; + rebuildDelegateValidator( + validatorModel.getType(), + context, + scopes + ); + + firePropertyChange(CONTEXT_PROPERTY, + oldContext, + context + ); + } + + public Set<NuitonValidatorScope> getScopes() { + return delegate.getModel().getScopes(); + } + + public void setScopes(NuitonValidatorScope... scopes) { + + Set<NuitonValidatorScope> oldScopes = getScopes(); + + NuitonValidatorModel<O> validatorModel = delegate.getModel(); + + rebuildDelegateValidator( + validatorModel.getType(), + validatorModel.getContext(), + scopes + ); + + firePropertyChange(SCOPES_PROPERTY, + oldScopes, + scopes + ); + } + + protected void rebuildDelegateValidator(Class<O> beanType, + String context, + NuitonValidatorScope... scopes) { + if (scopes.length == 0) { + scopes = NuitonValidatorScope.values(); + } + + // compute the new validator model + NuitonValidatorModel<O> validatorModel = validatorProvider.getModel(beanType, + context, + scopes + ); + // remove old delegate validator - delegate = null; + delegate = validatorProvider.newValidator(validatorModel); // changing context could change fields definition // so dettach bean, must rebuild the fields if (bean != null) { setBean(null); } + } - pcs.firePropertyChange(CONTEXT_PROPERTY, - oldContext, - context - ); + public BeanValidator<?> getParentValidator() { + return parentValidator; + } + public void setParentValidator(BeanValidator<?> parentValidator) { + this.parentValidator = parentValidator; } + public boolean hasFatalErrors() { + boolean result = messages != null && messages.hasFatalMessages(); + return result; + } + + public boolean hasErrors() { + boolean result = messages != null && messages.hasErrorMessagess(); + return result; + } + + public boolean hasWarnings() { + boolean result = messages != null && messages.hasWarningMessages(); + return result; + } + + public boolean hasInfos() { + boolean result = messages != null && messages.hasInfoMessages(); + return result; + } + /** + * Test a the validator contains the field given his name + * + * @param fieldName the name of the searched field + * @return <code>true</code> if validator contaisn this field, + * <code>false</code> otherwise + */ + public boolean containsField(String fieldName) { + Set<String> effectiveFields = getDelegate().getEffectiveFields(); + boolean result = effectiveFields.contains(fieldName); + return result; + } + + public boolean isValid(String fieldName) { + if (messages == null) { + + // no message, so this is valid + return true; + } + + // field is valid if no fatal messages nor error messages + boolean result = !( + messages.hasMessagesForScope(fieldName, NuitonValidatorScope.FATAL) || + messages.hasMessagesForScope(fieldName, NuitonValidatorScope.ERROR)); + + return result; + } + + /** * Convert a value. * <p/> * If an error occurs, then add an error in validator. @@ -392,21 +552,21 @@ mergeMessages(result); -// if (parentValidator != null) { -// // chained validation -// // the parent validator should not be changed from this validation -// boolean wasModified = parentValidator.isChanged(); -// parentValidator.doValidate(); -// if (!wasModified) { -// // push back old state -// parentValidator.setChanged(false); -// } -// } + if (parentValidator != null) { + // chained validation + // the parent validator should not be changed from this validation + boolean wasModified = parentValidator.isChanged(); + parentValidator.doValidate(); + if (!wasModified) { + // push back old state + parentValidator.setChanged(false); + } + } } @Override public String toString() { - return super.toString() + "<beanClass:" + initialValidatorModel.getType() + + return super.toString() + "<beanClass:" + delegate.getModel().getType() + ", context:" + getContext() + ">"; } @@ -440,6 +600,12 @@ pcs.removePropertyChangeListener(propertyName, listener); } + public void firePropertyChange(String propertyName, + Object oldValue, + Object newValue) { + pcs.firePropertyChange(propertyName, oldValue, newValue); + } + protected void fireFieldChanged(String field, NuitonValidatorScope scope, String[] toAdd, @@ -472,6 +638,7 @@ for (NuitonValidatorScope scope : scopes) { + // do the merge at scope level mergeMessages(scope, newMessages); } @@ -522,8 +689,6 @@ List<String> oldFields = messages.getFieldsForScope(scope); - Set<String> mergedFields = new HashSet<String>(); - Iterator<String> itr; // detects field with only new messages @@ -555,10 +720,6 @@ // treated field itr.remove(); - } else { - - // merged field - mergedFields.add(oldField); } } @@ -589,9 +750,6 @@ } protected NuitonValidator<O> getDelegate() { - if (delegate == null) { - delegate = validatorProvider.newValidator(validatorModel); - } return delegate; } Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorFactory.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorFactory.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorFactory.java 2011-01-24 13:08:51 UTC (rev 2040) @@ -0,0 +1,90 @@ +package org.nuiton.validator.bean; + +import org.nuiton.validator.NuitonValidator; +import org.nuiton.validator.NuitonValidatorFactory; +import org.nuiton.validator.NuitonValidatorProvider; +import org.nuiton.validator.NuitonValidatorScope; + +/** + * Factory of {@link BeanValidator}. + * + * To obtain a new {@link BeanValidator}, use one of the method + * + * <pre> + * BeanValidatorFactory.newBeanValidator(XXX) + * </pre> + * + * @author tchemit <chemit@codelutin.com> + * @since 2.0 + * @see BeanValidator + */ +public class BeanValidatorFactory { + + /** + * Obtain a new {@link BeanValidator} for the given parameters. + * <p/> + * <b>Note:</b> 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 <O> type of bean to validate + * @return the new instanciated {@link BeanValidator}. + * @throws NullPointerException if type is {@code null} + * @see NuitonValidatorFactory#getDefaultProviderName() + */ + public static <O> BeanValidator<O> newBeanValidator(Class<O> type, + String context, + NuitonValidatorScope... scopes) throws NullPointerException { + + + // get the provider default name + String providerName = NuitonValidatorFactory.getDefaultProviderName(); + + // get the bean validator with this provider + BeanValidator<O> beanValidator = newBeanValidator(providerName, + type, + context, + scopes + ); + return beanValidator; + + } + + + /** + * Obtain a new {@link BeanValidator} for the given parameters. + * <p/> + * <b>Note:</b> 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 <O> type of bean to validate + * @return the new instanciated {@link BeanValidator}. + * @throws NullPointerException if type is {@code null} + * @see NuitonValidatorFactory#getProvider(String) + */ + public static <O> BeanValidator<O> newBeanValidator(String providerName, + Class<O> type, + String context, + NuitonValidatorScope... scopes) throws NullPointerException { + + if (type == null) { + throw new NullPointerException( + "type parameter can not be null."); + } + + NuitonValidatorProvider provider = NuitonValidatorFactory.getProvider(providerName); + + BeanValidator<O> beanValidator = new BeanValidator<O>(provider, + type, + context, + scopes + ); + return beanValidator; + + } +} Property changes on: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorFactory.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: svn:eol-style + native Modified: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorListener.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorListener.java 2011-01-24 12:54:50 UTC (rev 2039) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorListener.java 2011-01-24 13:08:51 UTC (rev 2040) @@ -28,9 +28,11 @@ import java.util.EventListener; /** - * The listener contract to be used on {@link BeanValidator}. + * The listener contract to be used on {@link BeanValidator} to fire that + * some messages has changed for a given field and scope. * * @author tchemit <chemit@codelutin.com> + * @see BeanValidatorEvent * @since 2.0 */ public interface BeanValidatorListener extends EventListener { Copied: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorUtil.java (from rev 2035, trunk/nuiton-validator/src/main/java/org/nuiton/validator/legacy/BeanValidatorUtil.java) =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorUtil.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorUtil.java 2011-01-24 13:08:51 UTC (rev 2040) @@ -0,0 +1,131 @@ +/* + * #%L + * Nuiton Utils :: Nuiton Validator + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, 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 + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.validator.bean; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.validator.NuitonValidatorScope; + +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.List; + +/** + * The helper class for validation module. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.0 + */ +public class BeanValidatorUtil { + + /** Logger */ + static private final Log log = LogFactory.getLog(BeanValidatorUtil.class); + + protected BeanValidatorUtil() { + // no instance + } + + /** + * Convert a value to a given type and then if was succesffull try to set it + * in the bean manage by the validator. + * + * @param validator validator to be involved + * @param fieldName the name of the bean property + * @param value the actual value to convert + * @param valueClass the type of the conversion + */ + public static void convert(BeanValidator<?> validator, + String fieldName, + String value, + Class<?> valueClass) { + + Object result = validator.convert(fieldName, value, valueClass); + if (result != null) { + try { + BeanInfo info = + Introspector.getBeanInfo(validator.getBean().getClass()); + + for (PropertyDescriptor descriptor : + info.getPropertyDescriptors()) { + if (fieldName.equals(descriptor.getName()) && + descriptor.getWriteMethod() != null) { + + descriptor.getWriteMethod().invoke( + validator.getBean(), + result + ); + break; + } + } + } catch (Exception e) { + if (log.isErrorEnabled()) { + log.error("could not obtain beanInfo for " + + valueClass.getClass() + ", reason : " + + e.getMessage(), e); + } + } + } else { + //fixme : conversion failed, we should be able to notify ui + // that values has changed ? + // otherwise, bean value has not changed,... + } + } + + public static EnumSet<NuitonValidatorScope> getScopes( + List<BeanValidatorMessage<?>> messages) { + EnumSet<NuitonValidatorScope> result = + EnumSet.noneOf(NuitonValidatorScope.class); + for (BeanValidatorMessage<?> m : messages) { + result.add(m.getScope()); + } + return result; + } + + public static EnumMap<NuitonValidatorScope, Integer> getScopesCount( + List<BeanValidatorMessage<?>> messages) { + EnumMap<NuitonValidatorScope, Integer> result = + new EnumMap<NuitonValidatorScope, Integer>(NuitonValidatorScope.class); + for (NuitonValidatorScope s : NuitonValidatorScope.values()) { + result.put(s, 0); + } + for (BeanValidatorMessage<?> m : messages) { + + NuitonValidatorScope scope = m.getScope(); + + result.put(scope, result.get(scope) + 1); + } + + for (NuitonValidatorScope s : NuitonValidatorScope.values()) { + if (result.get(s) == 0) { + result.remove(s); + } + } + return result; + } + +} Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/package-info.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/package-info.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/package-info.java 2011-01-24 13:08:51 UTC (rev 2040) @@ -0,0 +1,61 @@ +/* + * #%L + * Nuiton Utils :: Nuiton Validator + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin + * %% + * 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 + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.validator.bean; +/** + * Package of Nuiton- beanValidator api. + * + * <h1>The <b>BeanValidator</b> api</h1> + * <p> + * The {@link BeanValidator} purpose is to validate a bean, with a listener api + * to interact with outside world. + * </p> + * It is mainly used in GUI parts of an application (Jaxx-validator use it). + * <br/> + * The idea is to attach the bean to validate insed the validator, then the + * validator listen any modification of the bean to revalidate it and fires + * events when messages has changed on a field. + * + * <pre> + * BeanValidatorListener listener = new BeanValidatorListener() {XXX}; + * BeanValidator<O> validator = XXX; + * validator.addBeanValidatorListener(listener); + * validator.setBean(o); + * </pre> + * + * <h2>Obtain a validator</h2> + * To obtain a bean validator use the factory of validators + * ({@link BeanValidatorFactory}. + * <br/> + * <pre> + * BeanValidator<O> validator = BeanValidatorFactory.newBeanValidator(O.class); + * </pre> + * + * <strong>To be continued...</strong> + * + * @since 2.0 + */ + +import org.nuiton.validator.bean.BeanValidatorFactory; +import org.nuiton.validator.bean.BeanValidator; Property changes on: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/package-info.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: svn:eol-style + native Modified: trunk/nuiton-validator/src/main/java/org/nuiton/validator/xwork2/XWork2ValidatorUtil.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/xwork2/XWork2ValidatorUtil.java 2011-01-24 12:54:50 UTC (rev 2039) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/xwork2/XWork2ValidatorUtil.java 2011-01-24 13:08:51 UTC (rev 2040) @@ -35,6 +35,7 @@ import com.opensymphony.xwork2.validator.Validator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.nuiton.util.beans.BeanUtil; import org.nuiton.validator.NuitonValidatorScope; import java.util.HashSet; @@ -139,7 +140,7 @@ ActionValidatorManager validatorManager = container.getInstance(ActionValidatorManager.class, "no-annotations"); - + return validatorManager; } @@ -148,24 +149,34 @@ NuitonValidatorScope[] scopeUniverse) { + Set<String> availableFields = BeanUtil.getReadableProperties(type); + ActionValidatorManager validatorManager = newValidationManager(null); Map<NuitonValidatorScope, String[]> fields = new TreeMap<NuitonValidatorScope, String[]>(); for (NuitonValidatorScope scope : scopeUniverse) { - Set<String> fieldNames = detectFieldsForScope(validatorManager, type, scope, context, false); + Set<String> fieldNames = detectFieldsForScope(validatorManager, + type, + scope, + context, + availableFields, + false + ); if (log.isDebugEnabled()) { - log.debug("detected validator fields for scope " + scope + ":" + context + - " : " + fieldNames); + log.debug("detected validator fields for scope " + scope + + ":" + context + " : " + fieldNames); } if (!fieldNames.isEmpty()) { // fields detected in this validator, keep it - fields.put(scope, fieldNames.toArray(new String[fieldNames.size()])); + fields.put(scope, + fieldNames.toArray(new String[fieldNames.size()]) + ); } } @@ -177,9 +188,9 @@ Class<?> type, NuitonValidatorScope scope, String context, + Set<String> availableFields, boolean includeDefaultContext) { - String scopeContext = getContextForScope(context, scope); @@ -197,6 +208,7 @@ } for (Validator<?> v : validator.getValidators(type, scopeContext)) { + // we only work on FieldValidator at the moment if (v instanceof FieldValidator) { if (skip > 0) { @@ -209,7 +221,19 @@ fieldValidator.getFieldName()); } String fName = fieldValidator.getFieldName(); - fields.add(fName); + if (availableFields.contains(fName)) { + + // safe field + fields.add(fName); + } else { + + // not a readable property, can not add it + String message = "Field " + fName + " in scope [" + scopeContext + "] is not a readable property of " + type.getName(); + if (log.isErrorEnabled()) { + log.error(message); + } + throw new IllegalStateException(message); + } } } Modified: trunk/nuiton-validator/src/test/java/org/nuiton/validator/AbstractValidatorDetectorTest.java =================================================================== --- trunk/nuiton-validator/src/test/java/org/nuiton/validator/AbstractValidatorDetectorTest.java 2011-01-24 12:54:50 UTC (rev 2039) +++ trunk/nuiton-validator/src/test/java/org/nuiton/validator/AbstractValidatorDetectorTest.java 2011-01-24 13:08:51 UTC (rev 2040) @@ -1,3 +1,27 @@ +/* + * #%L + * Nuiton Utils :: Nuiton Validator + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin + * %% + * 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 + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ package org.nuiton.validator; import org.junit.After; Modified: trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/BeanValidatorTest.java =================================================================== --- trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/BeanValidatorTest.java 2011-01-24 12:54:50 UTC (rev 2039) +++ trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/BeanValidatorTest.java 2011-01-24 13:08:51 UTC (rev 2040) @@ -30,8 +30,6 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.nuiton.validator.NuitonValidatorFactory; -import org.nuiton.validator.NuitonValidatorProvider; import org.nuiton.validator.NuitonValidatorScope; import org.nuiton.validator.xwork2.XWork2NuitonValidatorProvider; @@ -59,9 +57,14 @@ @Before public void setUp() { - NuitonValidatorProvider provider = NuitonValidatorFactory.getProvider(XWork2NuitonValidatorProvider.PROVIDER_NAME); + bean = new SimpleBean(); - validator = new BeanValidator<SimpleBean>(provider, SimpleBean.class, null); + } + + protected void prepareValidator(String context) { + + validator = BeanValidatorFactory.newBeanValidator(XWork2NuitonValidatorProvider.PROVIDER_NAME, SimpleBean.class, context); + validator.addBeanValidatorListener(fatalListener = new BeanValidatorListenerImpl(NuitonValidatorScope.FATAL)); validator.addBeanValidatorListener(errorListener = new BeanValidatorListenerImpl(NuitonValidatorScope.ERROR)); validator.addBeanValidatorListener(warningListener = new BeanValidatorListenerImpl(NuitonValidatorScope.WARNING)); @@ -89,9 +92,19 @@ private static final String INT_VALUE_INFO = "intValue.info"; + @Test(expected = IllegalStateException.class) + public void testValidateWithBad() { + + // with marchepo context, there is a unknown field in scope error + + prepareValidator("marchepo"); + } + @Test public void testValidate() { + prepareValidator(null); + assertMessages(fatalListener); assertMessages(errorListener); assertMessages(warningListener); @@ -176,6 +189,8 @@ public void testConvert() { + prepareValidator(null); + assertMessages(errorListener); assertMessages(warningListener); assertMessages(infoListener); Modified: trunk/nuiton-validator/src/test/java/org/nuiton/validator/model/ModelValidatorDetectorTestImpl.java =================================================================== --- trunk/nuiton-validator/src/test/java/org/nuiton/validator/model/ModelValidatorDetectorTestImpl.java 2011-01-24 12:54:50 UTC (rev 2039) +++ trunk/nuiton-validator/src/test/java/org/nuiton/validator/model/ModelValidatorDetectorTestImpl.java 2011-01-24 13:08:51 UTC (rev 2040) @@ -1,3 +1,27 @@ +/* + * #%L + * Nuiton Utils :: Nuiton Validator + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin + * %% + * 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 + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ package org.nuiton.validator.model; import org.junit.Assert; @@ -39,7 +63,7 @@ NuitonValidator<?> validator; Iterator<NuitonValidator<?>> iterator; - // test with all context and all scopes : two validators (Person + Pet + Pet (context)) + // test with all context and all scopes : 3 validators (Person + Pet + Pet (context)) result = detectValidators(Person.class, Pet.class); @@ -52,14 +76,18 @@ ValidatorTestHelper.assertValidatorModel(validator, null, Person.class, NuitonValidatorScope.values()); ValidatorTestHelper.assertValidatorEffectiveScopes(validator, NuitonValidatorScope.ERROR, NuitonValidatorScope.WARNING); + ValidatorTestHelper.assertValidatorEffectiveFields(validator, NuitonValidatorScope.ERROR, Person.PROPERTY_NAME, Person.PROPERTY_FIRSTNAME); + ValidatorTestHelper.assertValidatorEffectiveFields(validator, NuitonValidatorScope.WARNING, Person.PROPERTY_PET); validator = iterator.next(); ValidatorTestHelper.assertValidatorModel(validator, null, Pet.class, NuitonValidatorScope.values()); ValidatorTestHelper.assertValidatorEffectiveScopes(validator, NuitonValidatorScope.ERROR); + ValidatorTestHelper.assertValidatorEffectiveFields(validator, NuitonValidatorScope.ERROR, Pet.PROPERTY_NAME); validator = iterator.next(); ValidatorTestHelper.assertValidatorModel(validator, CONTEXT, Pet.class, NuitonValidatorScope.values()); ValidatorTestHelper.assertValidatorEffectiveScopes(validator, NuitonValidatorScope.INFO); + ValidatorTestHelper.assertValidatorEffectiveFields(validator, NuitonValidatorScope.INFO, Pet.PROPERTY_NAME); } @@ -82,6 +110,7 @@ validator = iterator.next(); ValidatorTestHelper.assertValidatorModel(validator, null, Person.class, NuitonValidatorScope.WARNING); ValidatorTestHelper.assertValidatorEffectiveScopes(validator, NuitonValidatorScope.WARNING); + ValidatorTestHelper.assertValidatorEffectiveFields(validator, NuitonValidatorScope.WARNING, Person.PROPERTY_PET); // test with no context and only fatal scope : no validator @@ -113,6 +142,7 @@ validator = iterator.next(); ValidatorTestHelper.assertValidatorModel(validator, CONTEXT, Pet.class, NuitonValidatorScope.values()); ValidatorTestHelper.assertValidatorEffectiveScopes(validator, NuitonValidatorScope.INFO); + ValidatorTestHelper.assertValidatorEffectiveFields(validator, NuitonValidatorScope.INFO, Pet.PROPERTY_NAME); // test with specific context fake and all scopes : no validator @@ -147,6 +177,7 @@ validator = iterator.next(); ValidatorTestHelper.assertValidatorModel(validator, CONTEXT, Pet.class, NuitonValidatorScope.FATAL, NuitonValidatorScope.INFO); ValidatorTestHelper.assertValidatorEffectiveScopes(validator, NuitonValidatorScope.INFO); + ValidatorTestHelper.assertValidatorEffectiveFields(validator, NuitonValidatorScope.INFO, Pet.PROPERTY_NAME); // test with specific context fake and fatal scope : no validator Modified: trunk/nuiton-validator/src/test/resources/org/nuiton/validator/bean/SimpleBean-error-validation.xml =================================================================== --- trunk/nuiton-validator/src/test/resources/org/nuiton/validator/bean/SimpleBean-error-validation.xml 2011-01-24 12:54:50 UTC (rev 2039) +++ trunk/nuiton-validator/src/test/resources/org/nuiton/validator/bean/SimpleBean-error-validation.xml 2011-01-24 13:08:51 UTC (rev 2040) @@ -23,21 +23,21 @@ #L% --> <!DOCTYPE validators PUBLIC - "-//OpenSymphony Group//XWork Validator 1.0.2//EN" - "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> + "-//OpenSymphony Group//XWork Validator 1.0.2//EN" + "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators> - <field name="stringValue"> - <field-validator type="requiredstring"> - <message>stringValue.error</message> - </field-validator> - </field> + <field name="stringValue"> + <field-validator type="requiredstring"> + <message>stringValue.error</message> + </field-validator> + </field> - <field name="intValue"> - <field-validator type="int"> - <param name="min">1</param> - <message>intValue.error</message> - </field-validator> - </field> + <field name="intValue"> + <field-validator type="int"> + <param name="min">1</param> + <message>intValue.error</message> + </field-validator> + </field> </validators> \ No newline at end of file Copied: trunk/nuiton-validator/src/test/resources/org/nuiton/validator/bean/SimpleBean-marchepo-error-validation.xml (from rev 2034, trunk/nuiton-validator/src/test/resources/org/nuiton/validator/bean/SimpleBean-error-validation.xml) =================================================================== --- trunk/nuiton-validator/src/test/resources/org/nuiton/validator/bean/SimpleBean-marchepo-error-validation.xml (rev 0) +++ trunk/nuiton-validator/src/test/resources/org/nuiton/validator/bean/SimpleBean-marchepo-error-validation.xml 2011-01-24 13:08:51 UTC (rev 2040) @@ -0,0 +1,38 @@ +<!-- + #%L + Nuiton Utils :: Nuiton Validator + + $Id$ + $HeadURL$ + %% + Copyright (C) 2011 CodeLutin, 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 + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> +<!DOCTYPE validators PUBLIC + "-//OpenSymphony Group//XWork Validator 1.0.2//EN" + "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> +<validators> + + <!-- add a unknown field --> + + <field name="stringValueUnknown"> + <field-validator type="requiredstring"> + <message>stringValue.error.can.not.happen!</message> + </field-validator> + </field> + +</validators> \ No newline at end of file