r2377 - in trunk/nuiton-validator: . src/main/java/org/nuiton/validator src/main/java/org/nuiton/validator/bean src/main/java/org/nuiton/validator/bean/list src/main/java/org/nuiton/validator/bean/simple src/main/java/org/nuiton/validator/xwork2 src/test/java/org/nuiton/validator/bean src/test/java/org/nuiton/validator/bean/list src/test/java/org/nuiton/validator/bean/simple src/test/java/org/nuiton/validator/xwork2 src/test/java/org/nuiton/validator/xwork2/field
Author: tchemit Date: 2012-07-27 16:16:19 +0200 (Fri, 27 Jul 2012) New Revision: 2377 Url: http://nuiton.org/repositories/revision/nuiton-utils/2377 Log: fixes #2209: Deprecate the BeanValidator api fixes #2208: Introduce a SimpleBeanValidator api fixes #2205: Introduce a BeanListValidator api Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/AbstractNuitonValidatorContext.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/AbstractValidator.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/AbstractValidatorEvent.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/ValidatorCreator.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/ValidatorListener.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidator.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidatorEvent.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidatorListener.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidatorMessage.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/package-info.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidator.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorEvent.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorListener.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorMessage.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/package-info.java trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/list/ trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/list/BeanListValidatorTest.java trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/simple/ trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorTest.java Modified: trunk/nuiton-validator/pom.xml trunk/nuiton-validator/src/main/java/org/nuiton/validator/NuitonValidatorFactory.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/NuitonValidatorResult.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidator.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorEvent.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorFactory.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorListener.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorMessage.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorUtil.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/xwork2/XWork2NuitonValidatorProvider.java trunk/nuiton-validator/src/main/java/org/nuiton/validator/xwork2/XWork2ValidatorUtil.java trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/BeanValidatorTest.java trunk/nuiton-validator/src/test/java/org/nuiton/validator/xwork2/XWork2NuitonValidatorProviderTest.java trunk/nuiton-validator/src/test/java/org/nuiton/validator/xwork2/field/AbstractFieldValidatorTest.java Modified: trunk/nuiton-validator/pom.xml =================================================================== --- trunk/nuiton-validator/pom.xml 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/pom.xml 2012-07-27 14:16:19 UTC (rev 2377) @@ -65,6 +65,11 @@ </dependency> <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + + <dependency> <groupId>org.nuiton.i18n</groupId> <artifactId>nuiton-i18n</artifactId> </dependency> Modified: trunk/nuiton-validator/src/main/java/org/nuiton/validator/NuitonValidatorFactory.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/NuitonValidatorFactory.java 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/NuitonValidatorFactory.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -47,7 +47,7 @@ public class NuitonValidatorFactory { /** Logger. */ - static private final Log log = + private static final Log log = LogFactory.getLog(NuitonValidatorFactory.class); protected static String defaultProviderName; Modified: trunk/nuiton-validator/src/main/java/org/nuiton/validator/NuitonValidatorResult.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/NuitonValidatorResult.java 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/NuitonValidatorResult.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -36,7 +36,8 @@ import java.util.TreeMap; /** - * Default implementation of the {@link NuitonValidatorResult} contract. + * Contains validation messages coming from the method + * {@link NuitonValidator#validate(Object)}. * * @author tchemit <chemit@codelutin.com> * @since 2.0 @@ -56,6 +57,14 @@ return !hasFatalMessages() && !hasErrorMessagess(); } + public void clear() { + messages.clear(); + } + + public boolean isEmpty() { + return messages.isEmpty(); + } + public boolean hasMessagesForScope(NuitonValidatorScope scope) { boolean result = false; if (messages != null) { Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/AbstractNuitonValidatorContext.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/AbstractNuitonValidatorContext.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/AbstractNuitonValidatorContext.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,366 @@ +package org.nuiton.validator.bean; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.apache.commons.beanutils.ConversionException; +import org.apache.commons.beanutils.Converter; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.util.converter.ConverterUtil; +import org.nuiton.validator.NuitonValidator; +import org.nuiton.validator.NuitonValidatorResult; +import org.nuiton.validator.NuitonValidatorScope; +import org.nuiton.validator.bean.list.BeanListValidator; + +import java.beans.Introspector; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Defines a context of validation used for a single bean. + * <p/> + * {@link BeanValidator} will then used one of this object and + * {@link BeanListValidator} as many as it contains beans. + * <p/> + * This object box a {@link NuitonValidator} to get validation state each time + * a higher validator requires it. + * <p/> + * It also offers the way to create events (merge logic + * + * @param <O> type of bean to validate + * @param <V> type of bean validator used + * @param <E> type of event to create + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public abstract class AbstractNuitonValidatorContext<O, V, E> { + + /** Logger. */ + private static final Log log = + LogFactory.getLog(AbstractNuitonValidatorContext.class); + + /** Bean to validate. */ + protected O bean; + + /** + * State of validation (keep all messages of validation for the filled + * bean). + */ + protected NuitonValidatorResult messages; + + /** Validator. */ + protected NuitonValidator<O> validator; + + /** map of conversion errors detected by this validator */ + protected final Map<String, String> conversionErrors; + + /** + * State to know if the validator can be used (we keep this state for + * performance reasons : do not want to compute this value each time a + * validation is asked...). + */ + protected boolean canValidate; + + protected abstract E createEvent(V source, + O bean, + String field, + NuitonValidatorScope scope, + String[] toAdd, + String[] toDelete); + + public AbstractNuitonValidatorContext() { + conversionErrors = Maps.newTreeMap(); + } + + public O getBean() { + return bean; + } + + public void setBean(O bean) { + if (log.isDebugEnabled()) { + log.debug(this + " : " + bean); + } + + // clean conversions of previous bean + conversionErrors.clear(); + this.bean = bean; + + setCanValidate(!validator.getEffectiveFields().isEmpty() && bean != null); + } + + public NuitonValidator<O> getValidator() { + return validator; + } + + public NuitonValidatorResult getMessages() { + return messages; + } + + public boolean isCanValidate() { + return canValidate; + } + + public void setCanValidate(boolean canValidate) { + this.canValidate = canValidate; + } + + public boolean isValid() { + return messages == null || messages.isValid(); + } + + 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; + } + + public boolean isValid(String fieldName) { + + // field is valid if no fatal messages nor error messages + boolean result = !( + messages.hasMessagesForScope(fieldName, NuitonValidatorScope.FATAL) || + messages.hasMessagesForScope(fieldName, NuitonValidatorScope.ERROR)); + + return result; + } + + public NuitonValidatorScope getHighestScope(String field) { + + NuitonValidatorScope scope = messages.getFieldHighestScope(field); + return scope; + } + + public void setValidator(NuitonValidator<O> validator) { + this.validator = validator; + } + + public NuitonValidatorResult validate() { + NuitonValidatorResult result = validator.validate(bean); + + // treate conversion errors + // reinject them + for (Map.Entry<String, String> entry : conversionErrors.entrySet()) { + + + // remove from validation, errors occurs on this field + String field = entry.getKey(); + + + List<String> errors = result.getErrorMessages(field); + + String conversionError = entry.getValue(); + if (errors != null) { + errors.clear(); + errors.add(conversionError); + } else { + errors = Collections.singletonList(conversionError); + } + + result.setMessagesForScope(NuitonValidatorScope.ERROR, field, errors); + } + return result; + } + + /** + * Convert a value. + * <p/> + * If an error occurs, then add an error in validator. + * + * @param <T> the type of conversion + * @param fieldName the name of the bean property + * @param value the value to convert + * @param valueClass the type of converted value + * @return the converted value, or null if conversion was not ok + */ + @SuppressWarnings({"unchecked"}) + public <T> T convert(String fieldName, String value, Class<T> valueClass) { + if (fieldName == null) { + throw new IllegalArgumentException("fieldName can not be null"); + } + if (valueClass == null) { + throw new IllegalArgumentException("valueClass can not be null"); + } + + // on ne convertit pas si il y a un bean et que le resultat de la + // validation pourra etre affiche quelque part + if (!isCanValidate() || value == null) { + return null; + } + + // remove the previous conversion error for the field + conversionErrors.remove(fieldName); + + T result; + try { + Converter converter = ConverterUtil.getConverter(valueClass); + if (converter == null) { + throw new RuntimeException( + "could not find converter for the type " + valueClass); + } + result = (T) converter.convert(valueClass, value); + /* Why this test ? if (result != null && !value.equals(result.toString())) { + conversionErrors.put(fieldName, "error.convertor." + Introspector.decapitalize(valueClass.getSimpleName())); + result = null; + validate(); + }*/ + } catch (ConversionException e) { + // get + String s = Introspector.decapitalize(valueClass.getSimpleName()); + conversionErrors.put(fieldName, "error.convertor." + s); + throw e; + } + return result; + } + + public List<E> mergeMessages(V beanValidator, + NuitonValidatorResult newMessages) { + + if (newMessages == null && messages == null) { + + // no messages ever registred and ask to delete them, so nothing + // to do + return null; + } + + Set<NuitonValidatorScope> scopes = getValidator().getEffectiveScopes(); + + // list of events to send after the merge of messages + List<E> events = Lists.newArrayList(); + + for (NuitonValidatorScope scope : scopes) { + + // do the merge at scope level + mergeMessages(beanValidator, scope, newMessages, events); + + } + + if (newMessages != null) { + + //TODO tchemit 2011-01-23 Perharps it will necessary to clear the messages for memory performance ? + + // finally keep the new messages as the current messages + this.messages = newMessages; + } + + return events; + } + + protected void mergeMessages(V beanValidator, + NuitonValidatorScope scope, + NuitonValidatorResult newMessages, + List<E> events) { + + + if (newMessages == null) { + + // special case to empty all messages + + List<String> fieldsForScope = messages.getFieldsForScope(scope); + + for (String field : fieldsForScope) { + List<String> messagesForScope = messages.getMessagesForScope(field, scope); + events.add(createEvent(beanValidator, bean, field, scope, null, messagesForScope.toArray(new String[messagesForScope.size()]))); + } + + // suppress all messages for this scope + messages.clearMessagesForScope(scope); + + + } else { + + List<String> newFields = newMessages.getFieldsForScope(scope); + + if (messages == null) { + + // first time of a merge, just add new messages + + for (String field : newFields) { + List<String> messagesForScope = newMessages.getMessagesForScope(field, scope); + events.add(createEvent(beanValidator, bean, field, scope, messagesForScope.toArray(new String[messagesForScope.size()]), null)); + } + + // nothing else to do + return; + } + + List<String> oldFields = messages.getFieldsForScope(scope); + + Iterator<String> itr; + + // detects field with only new messages + itr = newFields.iterator(); + while (itr.hasNext()) { + String newField = itr.next(); + + if (!oldFields.contains(newField)) { + + // this fields has now messages but not before : new messages + List<String> messagesForScope = newMessages.getMessagesForScope(newField, scope); + events.add(createEvent(beanValidator, bean, newField, scope, messagesForScope.toArray(new String[messagesForScope.size()]), null)); + + // treated field + itr.remove(); + } + } + + // detects fields with only obsolete messages + itr = oldFields.iterator(); + while (itr.hasNext()) { + String oldField = itr.next(); + + if (!newFields.contains(oldField)) { + + // this fields has no more messages + List<String> messagesForScope = messages.getMessagesForScope(oldField, scope); + events.add(createEvent(beanValidator, bean, oldField, scope, null, messagesForScope.toArray(new String[messagesForScope.size()]))); + + // treated field + itr.remove(); + } + } + + // now deal with mixte field (toAdd and toDelete) + for (String field : newFields) { + + List<String> newMessagesForScope = newMessages.getMessagesForScope(field, scope); + List<String> oldMessagesForScope = messages.getMessagesForScope(field, scope); + + // get old obsoletes messages to delete + Set<String> toDelete = new HashSet<String>(oldMessagesForScope); + toDelete.removeAll(newMessagesForScope); + + // get new messages to add + Set<String> toAdd = new HashSet<String>(newMessagesForScope); + toAdd.removeAll(oldMessagesForScope); + + events.add(createEvent( + beanValidator, + bean, + field, + scope, + toAdd.isEmpty() ? null : toAdd.toArray(new String[toAdd.size()]), + toDelete.isEmpty() ? null : toDelete.toArray(new String[toDelete.size()]) + )); + + } + } + } +} Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/AbstractValidator.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/AbstractValidator.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/AbstractValidator.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,367 @@ +package org.nuiton.validator.bean; + +import com.google.common.base.Preconditions; +import org.apache.commons.lang3.ObjectUtils; +import org.nuiton.util.beans.BeanUtil; +import org.nuiton.validator.NuitonValidator; +import org.nuiton.validator.NuitonValidatorModel; +import org.nuiton.validator.NuitonValidatorProvider; +import org.nuiton.validator.NuitonValidatorScope; + +import javax.swing.event.EventListenerList; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.util.Set; + +/** + * TODO + * + * @param <O> type of bean to validate + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public abstract class AbstractValidator<O> { + + /** + * Name of the bounded property {@code context}. + * + * @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}. + * + * @see #valid + * @see #isValid() + * @see #setValid(boolean) + */ + public static final String VALID_PROPERTY = "valid"; + + /** + * Name of the bounded property {@link #changed}. + * + * @see #changed + * @see #isChanged() + * @see #setChanged(boolean) + */ + public static final String CHANGED_PROPERTY = "changed"; + +// /** Logger. */ +// private static final Log log = LogFactory.getLog(BeanValidator.class); + +// /** +// * The delegate validator used to validate the bean. +// * +// * @since 2.5.2 +// */ +// protected NuitonValidator<O> delegate; + + /** + * State to indicate that validator has changed since the last time bean was + * setted. + */ + protected boolean changed; + + /** State of the validator (is true if no errors of error scope is found). */ + protected boolean valid = true; + + /** + * State to know if the validator can be used (we keep this state for + * performance reasons : do not want to compute this value each time a + * validation is asked...). + */ + protected boolean canValidate = true; + + /** Listener that listens on bean modification. */ + protected final PropertyChangeListener l; + + /** delegate property change support */ + protected final PropertyChangeSupport pcs; + + /** A list of event listeners for this validators */ + protected final EventListenerList listenerList = new EventListenerList(); + + /** + * The provider of delegate validators. + * <p/> + * It will also produce validator model. + * + * @see NuitonValidatorProvider + */ + protected final NuitonValidatorProvider validatorProvider; + + + protected AbstractValidator(NuitonValidatorProvider validatorProvider, + Class<O> beanClass) { + + // check if given bean class is Javabean compiliant + boolean javaBeanCompiliant = BeanUtil.isJavaBeanCompiliant(beanClass); + Preconditions.checkState( + javaBeanCompiliant, + 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); + +// // build delegate validator +// rebuildDelegateValidator( +// beanClass, +// context, +// scopes +// ); + +// // context has changed +// firePropertyChange(CONTEXT_PROPERTY, +// null, +// context +// ); +// +// // scopes has changed +// firePropertyChange(SCOPES_PROPERTY, +// null, +// scopes +// ); + + l = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + + O bean = (O) evt.getSource(); + + // the bean has changed, replay validation + doValidate(bean); + } + }; + } + + /** + * Obtain the {@link #changed} property value. + * <p/> + * Returns {@code true} if bean was modified since last + * time a bean was attached. + * + * @return {@code true} if bean was modified since last attachement of + * a bean. + */ + public boolean isChanged() { + return changed; + } + + /** + * To force the value of the property {@link #changed}. + * + * @param changed flag to force reset of property {@link #changed} + */ + public void setChanged(boolean changed) { + this.changed = changed; + + // force the property to be fired (never pass the older value) + firePropertyChange(CHANGED_PROPERTY, null, changed); + } + + public boolean isCanValidate() { + return canValidate; + } + + public void setCanValidate(boolean canValidate) { + 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) + firePropertyChange(VALID_PROPERTY, null, valid); + } + + + public String getContext() { + return getModel().getContext(); + } + + public void setContext(String context) { + + String oldContext = getContext(); + + if (ObjectUtils.equals(context, oldContext)) { + + // same context do nothing + return; + } + + NuitonValidatorModel<O> model = getModel(); + + // compute the new validator model + NuitonValidatorScope[] scopes = model.getScopes().toArray( + new NuitonValidatorScope[model.getScopes().size()]); + + rebuildDelegateValidator( + model.getType(), + context, + scopes + ); + + firePropertyChange(CONTEXT_PROPERTY, + oldContext, + context + ); + } + + public Set<NuitonValidatorScope> getScopes() { + return getModel().getScopes(); + } + + public Set<NuitonValidatorScope> getEffectiveScopes() { + return getDelegate().getEffectiveScopes(); + } + + public Set<String> getEffectiveFields() { + return getDelegate().getEffectiveFields(); + } + + public Set<String> getEffectiveFields(NuitonValidatorScope scope) { + return getDelegate().getEffectiveFields(scope); + } + + public void setScopes(NuitonValidatorScope... scopes) { + + Set<NuitonValidatorScope> oldScopes = getScopes(); + + rebuildDelegateValidator( + getModel().getType(), + getModel().getContext(), + scopes + ); + + firePropertyChange(SCOPES_PROPERTY, + oldScopes, + scopes + ); + } + + public abstract void doValidate(); + + public abstract boolean hasFatalErrors(); + + public abstract boolean hasErrors(); + + public abstract boolean hasWarnings(); + + public abstract boolean hasInfos(); + + public abstract boolean isValid(String fieldName); + + public abstract NuitonValidatorScope getHighestScope(String field); + + public abstract <T> T convert(O bean, String fieldName, String value, Class<T> valueClass); + + protected abstract void doValidate(O bean); + + protected abstract NuitonValidator<O> getDelegate(); + + protected abstract void rebuildDelegateValidator(Class<O> beanType, + String context, + NuitonValidatorScope... scopes); + + public Class<O> getType() { + return getModel().getType(); + } + + /** + * 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 void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + public void addPropertyChangeListener(String propertyName, + PropertyChangeListener listener) { + pcs.addPropertyChangeListener(propertyName, listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + public void removePropertyChangeListener(String propertyName, + PropertyChangeListener listener) { + pcs.removePropertyChangeListener(propertyName, listener); + } + + public void firePropertyChange(String propertyName, + Object oldValue, + Object newValue) { + pcs.firePropertyChange(propertyName, oldValue, newValue); + } + + protected NuitonValidatorModel<O> getModel() { + return getDelegate().getModel(); + } + +// protected NuitonValidator<O> rebuildDelegateValidator(Class<O> beanType, +// String context, +// NuitonValidatorScope... scopes) { +// +// // changing context could change fields definition +// // so dettach bean, must rebuild the fields +// +// // Dettach the bean before any thing, because with the new delegate +// // validator some old fields could not be used any longer, and then +// // listeners will never have the full reset of their model... +// +// // remove all validators. +// +// if (scopes == null || scopes.length == 0) { +// scopes = NuitonValidatorScope.values(); +// } +// +// // compute the new validator model +// NuitonValidatorModel<O> model = validatorProvider.getModel(beanType, +// context, +// scopes +// ); +// +// // remove old delegate validator +// NuitonValidator<O> delegate = validatorProvider.newValidator(model); +// } +} Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/AbstractValidatorEvent.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/AbstractValidatorEvent.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/AbstractValidatorEvent.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,62 @@ +package org.nuiton.validator.bean; + +import org.nuiton.validator.NuitonValidatorScope; + +import java.util.EventObject; + +/** + * TODO + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public abstract class AbstractValidatorEvent<V> extends EventObject { + + private static final long serialVersionUID = 1L; + + /** the field impacted by the validator */ + protected String field; + + /** the scope impacted by the event */ + protected NuitonValidatorScope scope; + + protected String[] messagestoAdd; + + protected String[] messagestoDelete; + + public abstract Object getBean(); + + public AbstractValidatorEvent(V source, + String field, + NuitonValidatorScope scope, + String[] messagestoAdd, + String[] messagestoDelete) { + super(source); + this.field = field; + this.scope = scope; + this.messagestoAdd = messagestoAdd; + this.messagestoDelete = messagestoDelete; + } + + @Override + public V getSource() { + return (V) super.getSource(); + } + + public String[] getMessagesToAdd() { + return messagestoAdd; + } + + public String[] getMessagesToDelete() { + return messagestoDelete; + } + + public NuitonValidatorScope getScope() { + return scope; + } + + public String getField() { + return field; + } + +} \ No newline at end of file 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 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidator.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -36,6 +36,7 @@ import org.nuiton.validator.NuitonValidatorProvider; import org.nuiton.validator.NuitonValidatorResult; import org.nuiton.validator.NuitonValidatorScope; +import org.nuiton.validator.bean.simple.SimpleBeanValidator; import javax.swing.event.EventListenerList; import java.beans.Introspector; @@ -67,7 +68,9 @@ * @author tchemit <chemit@codelutin.com> * @see BeanValidatorListener * @since 2.0 + * @deprecated since 2.5.2, use instead the {@link SimpleBeanValidator}. */ +@Deprecated public class BeanValidator<O> { /** @@ -430,7 +433,7 @@ setBean(null); } - if (scopes==null || scopes.length == 0) { + if (scopes == null || scopes.length == 0) { scopes = NuitonValidatorScope.values(); } Modified: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorEvent.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorEvent.java 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorEvent.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -25,6 +25,7 @@ package org.nuiton.validator.bean; import org.nuiton.validator.NuitonValidatorScope; +import org.nuiton.validator.bean.simple.SimpleBeanValidatorEvent; import java.util.EventObject; @@ -34,7 +35,9 @@ * * @author tchemit <chemit@codelutin.com> * @since 2.0 + * @deprecated since 2.5.2, use instead the {@link SimpleBeanValidatorEvent} */ +@Deprecated public class BeanValidatorEvent extends EventObject { private static final long serialVersionUID = 1L; Modified: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorFactory.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorFactory.java 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorFactory.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -30,6 +30,7 @@ import org.nuiton.validator.NuitonValidatorFactory; import org.nuiton.validator.NuitonValidatorProvider; import org.nuiton.validator.NuitonValidatorScope; +import org.nuiton.validator.bean.simple.SimpleBeanValidator; /** * Factory of {@link BeanValidator}. @@ -43,7 +44,9 @@ * @author tchemit <chemit@codelutin.com> * @see BeanValidator * @since 2.0 + * @deprecated since 2.5.2, use instead the {@link SimpleBeanValidator} factory methods. */ +@Deprecated public class BeanValidatorFactory { 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 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorListener.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -25,6 +25,8 @@ package org.nuiton.validator.bean; +import org.nuiton.validator.bean.simple.SimpleBeanValidatorListener; + import java.util.EventListener; /** @@ -34,7 +36,9 @@ * @author tchemit <chemit@codelutin.com> * @see BeanValidatorEvent * @since 2.0 + * @deprecated since 2.5.2, use instead the {@link SimpleBeanValidatorListener}. */ +@Deprecated public interface BeanValidatorListener extends EventListener { /** Modified: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorMessage.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorMessage.java 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorMessage.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -25,6 +25,7 @@ package org.nuiton.validator.bean; import org.nuiton.validator.NuitonValidatorScope; +import org.nuiton.validator.bean.simple.SimpleBeanValidatorMessage; import java.io.Serializable; import java.util.ArrayList; @@ -40,7 +41,9 @@ * method. * @author tchemit <chemit@codelutin.com> * @since 2.0 + * @deprecated since 2.5.2, use instead the {@link SimpleBeanValidatorMessage}. */ +@Deprecated public class BeanValidatorMessage<E extends BeanValidatorMessage<?>> implements Comparable<E>, Serializable { private static final long serialVersionUID = 1L; Modified: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorUtil.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorUtil.java 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorUtil.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -40,7 +40,9 @@ * * @author tchemit <chemit@codelutin.com> * @since 2.0 + * @deprecated since 2.5.2, for the moment none of this was used, and will no be replaced. */ +@Deprecated public class BeanValidatorUtil { /** Logger */ Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/ValidatorCreator.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/ValidatorCreator.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/ValidatorCreator.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,30 @@ +package org.nuiton.validator.bean; + +import org.nuiton.validator.NuitonValidatorProvider; +import org.nuiton.validator.NuitonValidatorScope; + +/** + * TODO + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public interface ValidatorCreator<V> { + + /** + * Given the parameters, instanciate a new {@link V}. + * + * @param provider the delegate validator provider + * @param type the type of object to validate + * @param context the context of validation (can be {@code null}) + * @param scopes scopes to use (if none given, will use all available scopes) + * @param <O> type of object to validate + * @return the new instance of bean validator + */ + <O> V newValidator(NuitonValidatorProvider provider, + Class<O> type, + String context, + NuitonValidatorScope... scopes + + ); +} Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/ValidatorListener.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/ValidatorListener.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/ValidatorListener.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,23 @@ +package org.nuiton.validator.bean; + +import org.nuiton.validator.bean.list.*; + +import java.util.EventListener; + +/** + * The definition of an event on {@link BeanListValidatorEvent} + * to be fired by a {@link BeanListValidator}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public interface ValidatorListener<E extends AbstractValidatorEvent<?>> extends EventListener { + + /** + * Invoked when the {@link E} detects some changes for a + * given bean / field / scope. + * + * @param event the event + */ + void onFieldChanged(E event); +} Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidator.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidator.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidator.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,538 @@ +package org.nuiton.validator.bean.list; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.apache.commons.beanutils.ConversionException; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.util.beans.BeanUtil; +import org.nuiton.validator.NuitonValidator; +import org.nuiton.validator.NuitonValidatorFactory; +import org.nuiton.validator.NuitonValidatorModel; +import org.nuiton.validator.NuitonValidatorProvider; +import org.nuiton.validator.NuitonValidatorResult; +import org.nuiton.validator.NuitonValidatorScope; +import org.nuiton.validator.bean.AbstractNuitonValidatorContext; +import org.nuiton.validator.bean.AbstractValidator; +import org.nuiton.validator.bean.BeanValidator; + +import java.beans.PropertyChangeListener; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * To validate a list of beans. + * <p/> + * Each bean of the list will be associated with a {@link BeanValidator}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public class BeanListValidator<O> extends AbstractValidator<O> { + + /** Logger. */ + private static final Log log = LogFactory.getLog(BeanValidator.class); + + /** + * Obtain a new {@link BeanListValidator} 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> BeanListValidator<O> newValidator(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 + BeanListValidator<O> beanValidator = newValidator(providerName, + type, + context, + scopes + ); + return beanValidator; + } + + /** + * Obtain a new {@link BeanListValidator} 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> BeanListValidator<O> newValidator(String providerName, + Class<O> type, + String context, + NuitonValidatorScope... scopes) throws NullPointerException { + + Preconditions.checkNotNull(type, "type parameter can not be null."); + + // get delegate validator provider + NuitonValidatorProvider provider = + NuitonValidatorFactory.getProvider(providerName); + + Preconditions.checkState( + provider != null, + "Could not find provider with name " + providerName); + + // create the new instance of bean validator + BeanListValidator<O> validator = new BeanListValidator<O>( + provider, type, context, scopes + ); + + return validator; + } + + /** + * Context for each bean registred. + * + * @since 2.5.2 + */ + protected final Map<O, NuitonValidatorContext<O>> contexts; + + /** + * The delegate validator used to validate the bean. + * + * @since 2.5.2 + */ + protected NuitonValidator<O> delegate; + + public BeanListValidator(NuitonValidatorProvider validatorProvider, + Class<O> beanClass, + String context) { + + this(validatorProvider, beanClass, + context, + NuitonValidatorScope.values() + ); + } + + public BeanListValidator(NuitonValidatorProvider validatorProvider, + Class<O> beanClass, + String context, + NuitonValidatorScope... scopes) { + + super(validatorProvider, beanClass); + + contexts = Maps.newHashMap(); + + // build delegate validator + rebuildDelegateValidator(beanClass, context, scopes); + + // context has changed + firePropertyChange(CONTEXT_PROPERTY, + null, + context + ); + + // scopes has changed + firePropertyChange(SCOPES_PROPERTY, + null, + scopes + ); + } + + /** + * Add a bean to validate. + * <p/> + * The bean can not be null, nor registered twice in the validator. + * + * @param bean the bean to attach (can not be {@code null}). + */ + public void addBean(O bean) { + + // bean can not be null + Preconditions.checkNotNull(bean); + + // can not register twice the same bean + Preconditions.checkState(!contexts.containsKey(bean), + "The bean " + bean + + " is already registred in this validator."); + + if (log.isDebugEnabled()) { + log.debug(this + " : " + bean); + } + + // create validator for this bean + NuitonValidator<O> validator = validatorProvider.newValidator(getModel()); + + // register it + NuitonValidatorContext<O> newcontext = + new NuitonValidatorContext<O>(bean, validator); + newcontext.setValidator(validator); + newcontext.setBean(bean); + + contexts.put(bean, newcontext); + + setCanValidate(isCanValidate() && newcontext.isCanValidate()); + + try { + + BeanUtil.addPropertyChangeListener(l, bean); + } catch (Exception eee) { + if (log.isInfoEnabled()) { + log.info("Can't register as listener for bean " + bean.getClass() + + " for reason " + eee.getMessage(), eee); + } + } + validate(bean); + + setChanged(false); + setValid(isValid0()); +// firePropertyChange(BEAN_PROPERTY, oldBean, bean); + } + + public void addAllBeans(Collection<O> beansToAdd) { + for (O bean : beansToAdd) { + addBean(bean); + } + } + + /** + * Remove the given bean from the validaotr. + * <p/> + * The bean can not be {@code null}, nor not has been previously + * registred in this validator. + * + * @param bean the bean to unregister + */ + public void removeBean(O bean) { + + // bean can not be null + Preconditions.checkNotNull(bean); + + if (log.isDebugEnabled()) { + log.debug(this + " : " + bean); + } + + // remove all messages for all fields of the validator + NuitonValidatorContext<O> context = getContext(bean); + + mergeMessages(context, null); + + contexts.remove(bean); + + try { + BeanUtil.removePropertyChangeListener(l, bean); + } catch (Exception eee) { + if (log.isInfoEnabled()) { + log.info("Can't unregister as listener for bean " + bean.getClass() + + " for reason " + eee.getMessage(), eee); + } + } + } + + /** + * Remove all the given beans fro this validator. + * <p/> + * Like in method {@link #removeBean(Object)}, each bean must be not + * {@code null} and has been previously registred in this validator. + * + * @param beansToRemove beans to remove from this validator + */ + public void removeAllBeans(Collection<O> beansToRemove) { + for (O bean : beansToRemove) { + removeBean(bean); + } + } + + /** + * Shortcut method to unregister all previously registred beans from + * this validator. + */ + public void removeAllBeans() { + Set<O> beansToRemove = getBeans(); + removeAllBeans(beansToRemove); + } + + @Override + public boolean hasFatalErrors() { + boolean result = false; + for (NuitonValidatorContext<O> context : contexts.values()) { + result = context.hasFatalErrors(); + if (result) { + break; + } + } + return result; + } + + @Override + public boolean hasErrors() { + boolean result = false; + for (NuitonValidatorContext<O> context : contexts.values()) { + result = context.hasErrors(); + if (result) { + break; + } + } + return result; + } + + @Override + public boolean hasWarnings() { + boolean result = false; + for (NuitonValidatorContext<O> context : contexts.values()) { + result = context.hasWarnings(); + if (result) { + break; + } + } + return result; + } + + @Override + public boolean hasInfos() { + boolean result = false; + for (NuitonValidatorContext<O> context : contexts.values()) { + result = context.hasInfos(); + if (result) { + break; + } + } + return result; + } + + @Override + public boolean isValid(String fieldName) { + boolean result = true; + + for (NuitonValidatorContext<O> context : contexts.values()) { + result = context.isValid(fieldName); + if (!result) { + break; + } + } + return result; + } + + @Override + public NuitonValidatorScope getHighestScope(String field) { + Set<NuitonValidatorScope> scopes = Sets.newHashSet(); + for (NuitonValidatorContext<O> context : contexts.values()) { + scopes.add(context.getHighestScope(field)); + } + NuitonValidatorScope scope = null; + if (scopes.isEmpty()) { + List<NuitonValidatorScope> scopeList = Lists.newArrayList(scopes); + Collections.sort(scopeList); + scope = scopeList.get(0); + } + return scope; + } + + @Override + public void doValidate() { + validate(); + setValid(isValid0()); + setChanged(true); + } + + /** + * Convert a value. + * <p/> + * If an error occurs, then add an error in validator. + * + * @param <T> the type of conversion + * @param fieldName the name of the bean property + * @param value the value to convert + * @param valueClass the type of converted value + * @return the converted value, or null if conversion was not ok + */ + @Override + public <T> T convert(O bean, + String fieldName, + String value, + Class<T> valueClass) { + NuitonValidatorContext<O> context = getContext(bean); + T convert = null; + try { + convert = context.convert(fieldName, value, valueClass); + } catch (ConversionException e) { + // must revalidate + validate(); + } + return convert; + } + + public void addBeanListValidatorListener(BeanListValidatorListener listener) { + listenerList.add(BeanListValidatorListener.class, listener); + } + + public void removeBeanListValidatorListener(BeanListValidatorListener listener) { + listenerList.remove(BeanListValidatorListener.class, listener); + } + + public BeanListValidatorListener[] getBeanListValidatorListeners() { + return listenerList.getListeners(BeanListValidatorListener.class); + } + + public Set<O> getBeans() { + return ImmutableSet.copyOf(contexts.keySet()); + } + + @Override + protected void doValidate(O bean) { + validate(bean); + setValid(isValid0()); + setChanged(true); + } + + @Override + protected NuitonValidator<O> getDelegate() { + return delegate; + } + + @Override + protected void rebuildDelegateValidator(Class<O> beanType, + String context, + NuitonValidatorScope... scopes) { + + // changing context could change fields definition + // so dettach bean, must rebuild the fields + + // Dettach the bean before any thing, because with the new delegate + // validator some old fields could not be used any longer, and then + // listeners will never have the full reset of their model... + + // remove all validators. + + if (scopes == null || scopes.length == 0) { + scopes = NuitonValidatorScope.values(); + } + + // compute the new validator model + NuitonValidatorModel<O> model = validatorProvider.getModel(beanType, + context, + scopes + ); + + // remove old delegate validator + delegate = validatorProvider.newValidator(model); + } + + /** + * 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} + * <p/> + * <b>Note:</b> la methode est protected et on utilise la methode + * {@link #doValidate()} car la méthode ne modifie pas les etats + * internes et cela en rend son utilisation delicate (le validateur entre + * dans un etat incoherent par rapport aux messages envoyés). + */ + protected 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 (isCanValidate()) { + + for (O bean : contexts.keySet()) { + + validate(bean); + } + } + } + + protected void validate(O bean) { + NuitonValidatorContext<O> validator = getContext(bean); + NuitonValidatorResult result = validator.validate(); + mergeMessages(validator, result); + } + + protected boolean isValid0() { + boolean result = true; + for (NuitonValidatorContext<O> context : contexts.values()) { + result = context.isValid(); + if (!result) { + break; + } + } + return result; + } + + protected void mergeMessages(NuitonValidatorContext<O> context, + NuitonValidatorResult newMessages) { + + List<BeanListValidatorEvent> events = context.mergeMessages( + this, newMessages); + + if (CollectionUtils.isNotEmpty(events)) { + + // send all messages + for (BeanListValidatorEvent event : events) { + fireFieldChanged(event); + } + } + } + + protected void fireFieldChanged(BeanListValidatorEvent evt) { + + for (BeanListValidatorListener listener : + listenerList.getListeners(BeanListValidatorListener.class)) { + listener.onFieldChanged(evt); + } + } + + protected NuitonValidatorContext<O> getContext(O bean) { + NuitonValidatorContext<O> context = contexts.get(bean); + Preconditions.checkState( + context != null, + "Bean " + bean + " was not register in this list validator"); + return context; + } + + + protected static class NuitonValidatorContext<O> extends AbstractNuitonValidatorContext<O, BeanListValidator<O>, BeanListValidatorEvent> { + + public NuitonValidatorContext(O bean, NuitonValidator<O> validator) { + setValidator(validator); + setBean(bean); + } + + @Override + protected BeanListValidatorEvent createEvent(BeanListValidator<O> source, + O bean, + String field, + NuitonValidatorScope scope, + String[] toAdd, + String[] toDelete) { + return new BeanListValidatorEvent( + source, + bean, + field, + scope, + toAdd, + toDelete + ); + } + } +} Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidatorEvent.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidatorEvent.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidatorEvent.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,33 @@ +package org.nuiton.validator.bean.list; + +import org.nuiton.validator.NuitonValidatorScope; +import org.nuiton.validator.bean.AbstractValidatorEvent; + +/** + * TODO + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public class BeanListValidatorEvent extends AbstractValidatorEvent<BeanListValidator<?>> { + + private static final long serialVersionUID = 1L; + + /** the bean on which event occurs. */ + protected Object bean; + + public BeanListValidatorEvent(BeanListValidator<?> source, + Object bean, + String field, + NuitonValidatorScope scope, + String[] messagestoAdd, + String[] messagestoDelete) { + super(source, field, scope, messagestoAdd, messagestoDelete); + this.bean = bean; + } + + public Object getBean() { + return bean; + } + +} \ No newline at end of file Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidatorListener.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidatorListener.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidatorListener.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,14 @@ +package org.nuiton.validator.bean.list; + +import org.nuiton.validator.bean.ValidatorListener; + +/** + * The definition of an event on {@link BeanListValidatorEvent} + * to be fired by a {@link BeanListValidator}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public interface BeanListValidatorListener extends ValidatorListener<BeanListValidatorEvent> { + +} Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidatorMessage.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidatorMessage.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/BeanListValidatorMessage.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,130 @@ +package org.nuiton.validator.bean.list; + +import org.nuiton.validator.NuitonValidatorScope; +import org.nuiton.validator.bean.BeanValidatorMessage; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import static org.nuiton.i18n.I18n._; + +/** + * TODO + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public class BeanListValidatorMessage<E extends BeanListValidatorMessage<?>> implements Comparable<E>, Serializable { + + private static final long serialVersionUID = 1L; + + /** the validator that produce the message */ + protected BeanListValidator<?> validator; + + /** the bean on which event occurs. */ + protected Object bean; + + /** the field that produce the message */ + protected String field; + + /** the label of the message (to be displayed somewhere) */ + protected String message; + + /** the scope of the message */ + protected NuitonValidatorScope scope; + + public BeanListValidatorMessage(BeanListValidator<?> validator, + Object bean, + String field, + String message, + NuitonValidatorScope scope) { + this.field = field; + this.bean = bean; + this.validator = validator; + this.message = message == null ? null : message.trim(); + this.scope = scope; + } + + public BeanListValidator<?> getValidator() { + return validator; + } + + public String getField() { + return field; + } + + public NuitonValidatorScope getScope() { + return scope; + } + + public String getMessage() { + return message; + } + + public Object getBean() { + return bean; + } + + @Override + public int compareTo(E o) { + // sort on scope + int result = getScope().compareTo(o.getScope()); + if (result == 0) { + // sort on field name + result = field.compareTo(o.field); + if (result == 0) { + // sort on message + result = message.compareTo(o.message); + } + } + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BeanValidatorMessage<?>)) { + return false; + } + + BeanListValidatorMessage<?> that = (BeanListValidatorMessage<?>) o; + + return field.equals(that.field) && + (message != null ? !message.equals(that.message) : that.message == null) && + scope == that.scope; + } + + @Override + public int hashCode() { + int result = field.hashCode(); + result = 31 * result + (bean != null ? bean.hashCode() : 0); + result = 31 * result + (message != null ? message.hashCode() : 0); + result = 31 * result + (scope != null ? scope.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return bean + "[" + scope + "] - " + getI18nError(message); + } + + public String getI18nError(String error) { + String text; + if (!error.contains("##")) { + text = _(error); + } else { + StringTokenizer stk = new StringTokenizer(error, "##"); + String errorName = stk.nextToken(); + List<String> args = new ArrayList<String>(); + while (stk.hasMoreTokens()) { + args.add(stk.nextToken()); + } + text = _(errorName, args.toArray()); + } + return text; + } +} \ No newline at end of file Copied: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/package-info.java (from rev 2376, trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/package-info.java) =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/package-info.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/list/package-info.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,70 @@ +/* + * #%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 of Nuiton- beanValidator api. + * + * <h1>The <b>BeanValidator</b> api</h1> + * <p> + * The {@link org.nuiton.validator.bean.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> + * BeanListValidatorListener listener = new BeanListValidatorListener() {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 org.nuiton.validator.bean.BeanValidatorFactory}. + * <br/> + * <pre> + * BeanValidator<O> validator = BeanValidatorFactory.newValidator(O.class); + * </pre> + * + * <h2>Using the {@link org.nuiton.validator.bean.BeanValidatorFactory.BeanValidatorCreator}</h2> + * + * It is possible to use a {@link org.nuiton.validator.bean.BeanValidatorFactory.BeanValidatorCreator} to change the + * implementation of {@link org.nuiton.validator.bean.BeanValidator} + * instanticated by the factory. + * + * For this just use the method + * {@link org.nuiton.validator.bean.BeanValidatorFactory#setCreator(BeanValidatorCreator)}. + * + * By default it will us a default creator + * ({@link org.nuiton.validator.bean.BeanValidatorFactory.DefaultBeanValidatorCreator}). + * + * <strong>To be continued...</strong> + * + * @since 2.0 + */ +package org.nuiton.validator.bean.list; \ No newline at end of file Copied: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidator.java (from rev 2376, trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidator.java) =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidator.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidator.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,479 @@ +/* + * #%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.simple; + +import com.google.common.base.Preconditions; +import org.apache.commons.beanutils.ConversionException; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.util.beans.BeanUtil; +import org.nuiton.validator.NuitonValidator; +import org.nuiton.validator.NuitonValidatorFactory; +import org.nuiton.validator.NuitonValidatorModel; +import org.nuiton.validator.NuitonValidatorProvider; +import org.nuiton.validator.NuitonValidatorResult; +import org.nuiton.validator.NuitonValidatorScope; +import org.nuiton.validator.bean.AbstractNuitonValidatorContext; +import org.nuiton.validator.bean.AbstractValidator; + +import java.beans.PropertyChangeListener; +import java.util.List; + +/** + * Validator for a javaBean object. + * <p/> + * A such validator is designed to validate to keep the validation of a bean, + * means the bean is attached to the validator (via the context field {@link #context}. + * <p/> + * 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 {@link SimpleBeanValidator} 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 SimpleBeanValidatorListener + * @since 2.5.2 + */ +public class SimpleBeanValidator<O> extends AbstractValidator<O> { + + /** + * Name of the bounded property {@code bean}. + * + * @see #getBean() + * @see #setBean(Object) + */ + public static final String BEAN_PROPERTY = "bean"; + + /** Logger. */ + private static final Log log = LogFactory.getLog(SimpleBeanValidator.class); + + /** + * Obtain a new {@link SimpleBeanValidator} 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 SimpleBeanValidator}. + * @throws NullPointerException if type is {@code null} + * @see NuitonValidatorFactory#getDefaultProviderName() + */ + public static <O> SimpleBeanValidator<O> newValidator( + 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 + SimpleBeanValidator<O> beanValidator = newValidator(providerName, + type, + context, + scopes + ); + return beanValidator; + } + + /** + * Obtain a new {@link SimpleBeanValidator} 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 SimpleBeanValidator}. + * @throws NullPointerException if type is {@code null} + * @see NuitonValidatorFactory#getProvider(String) + */ + public static <O> SimpleBeanValidator<O> newValidator( + String providerName, + Class<O> type, + String context, + NuitonValidatorScope... scopes) throws NullPointerException { + + Preconditions.checkNotNull(type, + "type parameter can not be null."); + + // get delegate validator provider + NuitonValidatorProvider provider = + NuitonValidatorFactory.getProvider(providerName); + + Preconditions.checkState( + provider != null, + "Could not find provider with name " + providerName); + + // create the new instance of bean validator + SimpleBeanValidator<O> validator = new SimpleBeanValidator<O>( + provider, type, context, scopes); + + return validator; + } + + /** + * Context of the registred bean to validate. + * + * @since 2.5.2 + */ + protected final NuitonValidatorContext<O> context; + + /** + * To chain to another validator (acting as parent of this one). + * + * @since 2.5.2 + */ + protected SimpleBeanValidator<?> parentValidator; + + public SimpleBeanValidator(NuitonValidatorProvider validatorProvider, + Class<O> beanClass, + String context) { + + this(validatorProvider, beanClass, + context, + NuitonValidatorScope.values() + ); + } + + public SimpleBeanValidator(NuitonValidatorProvider validatorProvider, + Class<O> beanClass, + String context, + NuitonValidatorScope... scopes) { + + super(validatorProvider, beanClass); + + this.context = new NuitonValidatorContext<O>(); + + // build delegate validator + rebuildDelegateValidator(beanClass, context, scopes); + + // context has changed + firePropertyChange(CONTEXT_PROPERTY, + null, + context + ); + + // scopes has changed + firePropertyChange(SCOPES_PROPERTY, + null, + scopes + ); + } + + /** + * 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 context.getBean(); + } + + /** + * Change the attached bean. + * <p/> + * As a side effect, the internal + * {@link AbstractNuitonValidatorContext#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 = getBean(); + if (log.isDebugEnabled()) { + log.debug(this + " : " + bean); + } + + if (oldBean != null) { + try { + BeanUtil.removePropertyChangeListener(l, oldBean); + } catch (Exception eee) { + if (log.isInfoEnabled()) { + log.info("Can't unregister as listener for bean " + oldBean.getClass() + + " for reason " + eee.getMessage(), eee); + } + } + } + context.setBean(bean); + + if (bean == null) { + + // remove all messages for all fields of the validator + + mergeMessages(null); + + } else { + try { + + BeanUtil.addPropertyChangeListener(l, bean); + } catch (Exception eee) { + if (log.isInfoEnabled()) { + log.info("Can't register as listener for bean " + bean.getClass() + + " for reason " + eee.getMessage(), eee); + } + } + validate(); + } + setChanged(false); + setValid(context.isValid()); + firePropertyChange(BEAN_PROPERTY, oldBean, bean); + } + + public SimpleBeanValidator<?> getParentValidator() { + return parentValidator; + } + + public void setParentValidator(SimpleBeanValidator<?> parentValidator) { + this.parentValidator = parentValidator; + } + + @Override + public boolean hasFatalErrors() { + boolean result = context.hasFatalErrors(); + return result; + } + + @Override + public boolean hasErrors() { + boolean result = context.hasErrors(); + return result; + } + + @Override + public boolean hasWarnings() { + boolean result = context.hasWarnings(); + return result; + } + + @Override + public boolean hasInfos() { + boolean result = context.hasInfos(); + return result; + } + + @Override + public boolean isValid(String fieldName) { + + // field is valid if no fatal messages nor error messages + boolean result = context.isValid(fieldName); + return result; + } + + @Override + public NuitonValidatorScope getHighestScope(String field) { + NuitonValidatorScope scope = context.getHighestScope(field); + return scope; + } + + @Override + public <T> T convert(O bean, String fieldName, String value, Class<T> valueClass) { + Preconditions.checkState( + ObjectUtils.equals(bean, getBean()), + "Can not validate the bean [" + bean + + "] which is not the one registred [" + bean + + "] in this validator."); + + T convert = convert(fieldName, value, valueClass); + return convert; + } + + @Override + public void doValidate() { + validate(); + setValid(context.isValid()); + setChanged(true); + } + + /** + * Convert a value. + * <p/> + * If an error occurs, then add an error in validator. + * + * @param <T> the type of conversion + * @param fieldName the name of the bean property + * @param value the value to convert + * @param valueClass the type of converted value + * @return the converted value, or null if conversion was not ok + */ + public <T> T convert(String fieldName, String value, Class<T> valueClass) { + T convert = null; + + try { + convert = context.convert(fieldName, value, valueClass); + } catch (ConversionException e) { + // must revalidate + validate(); + } + return convert; + } + + public void addSimpleBeanValidatorListener(SimpleBeanValidatorListener listener) { + listenerList.add(SimpleBeanValidatorListener.class, listener); + } + + public void removeSimpleBeanValidatorListener(SimpleBeanValidatorListener listener) { + listenerList.remove(SimpleBeanValidatorListener.class, listener); + } + + public SimpleBeanValidatorListener[] getSimpleBeanValidatorListeners() { + return listenerList.getListeners(SimpleBeanValidatorListener.class); + } + + @Override + protected void doValidate(O bean) { + + Preconditions.checkState( + ObjectUtils.equals(bean, getBean()), + "Can not validate the bean [" + bean + + "] which is not the one registred [" + bean + + "] in this validator."); + + doValidate(); + } + + @Override + protected NuitonValidator<O> getDelegate() { + return context.getValidator(); + } + + @Override + protected void rebuildDelegateValidator(Class<O> beanType, + String context, + NuitonValidatorScope... scopes) { + + // changing context could change fields definition + // so dettach bean, must rebuild the fields + + // Dettach the bean before any thing, because with the new delegate + // validator some old fields could not be used any longer, and then + // listeners will never have the full reset of their model... + if (getBean() != null) { + setBean(null); + } + + if (scopes == null || scopes.length == 0) { + scopes = NuitonValidatorScope.values(); + } + + // compute the new validator model + NuitonValidatorModel<O> validatorModel = validatorProvider.getModel(beanType, + context, + scopes + ); + + // remove old delegate validator + NuitonValidator<O> delegate = validatorProvider.newValidator(validatorModel); + this.context.setValidator(delegate); + } + + /** + * 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} + * <p/> + * <b>Note:</b> la methode est protected et on utilise la methode + * {@link #doValidate()} car la méthode ne modifie pas les etats + * internes et cela en rend son utilisation delicate (le validateur entre + * dans un etat incoherent par rapport aux messages envoyés). + */ + protected 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 (isCanValidate()) { + + NuitonValidatorResult result = context.validate(); + + 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); + } + } + } + } + + protected void fireFieldChanged(SimpleBeanValidatorEvent evt) { + + for (SimpleBeanValidatorListener listener : + listenerList.getListeners(SimpleBeanValidatorListener.class)) { + listener.onFieldChanged(evt); + } + } + + protected void mergeMessages(NuitonValidatorResult newMessages) { + + List<SimpleBeanValidatorEvent> events = context.mergeMessages(this, + newMessages); + + if (CollectionUtils.isNotEmpty(events)) { + + // send all messages + for (SimpleBeanValidatorEvent event : events) { + fireFieldChanged(event); + } + } + } + + protected static class NuitonValidatorContext<O> extends AbstractNuitonValidatorContext<O, SimpleBeanValidator<O>, SimpleBeanValidatorEvent> { + + @Override + protected SimpleBeanValidatorEvent createEvent(SimpleBeanValidator<O> source, + O bean, + String field, + NuitonValidatorScope scope, + String[] toAdd, + String[] toDelete) { + SimpleBeanValidatorEvent evt = new SimpleBeanValidatorEvent( + source, + field, + scope, + toAdd, + toDelete + ); + return evt; + } + } +} Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorEvent.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorEvent.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorEvent.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,28 @@ +package org.nuiton.validator.bean.simple; + +import org.nuiton.validator.NuitonValidatorScope; +import org.nuiton.validator.bean.AbstractValidatorEvent; + +/** + * TODO + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public class SimpleBeanValidatorEvent extends AbstractValidatorEvent<SimpleBeanValidator<?>> { + + private static final long serialVersionUID = 1L; + + public SimpleBeanValidatorEvent(SimpleBeanValidator<?> source, + String field, + NuitonValidatorScope scope, + String[] messagestoAdd, + String[] messagestoDelete) { + super(source, field, scope, messagestoAdd, messagestoDelete); + } + + @Override + public Object getBean() { + return getSource().getBean(); + } +} Added: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorListener.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorListener.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorListener.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,15 @@ +package org.nuiton.validator.bean.simple; + +import org.nuiton.validator.bean.list.BeanListValidator; +import org.nuiton.validator.bean.ValidatorListener; + +/** + * The definition of an event on {@link SimpleBeanValidatorEvent} + * to be fired by a {@link BeanListValidator}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public interface SimpleBeanValidatorListener extends ValidatorListener<SimpleBeanValidatorEvent> { + +} Copied: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorMessage.java (from rev 2376, trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/BeanValidatorMessage.java) =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorMessage.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorMessage.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,145 @@ +/* + * #%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.simple; + +import org.nuiton.validator.NuitonValidatorScope; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import static org.nuiton.i18n.I18n._; + +/** + * The object to box a validation message. + * + * @param <E> type of message (use for override {@link #compareTo(Object)} + * method. + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public class SimpleBeanValidatorMessage<E extends SimpleBeanValidatorMessage<?>> implements Comparable<E>, Serializable { + + private static final long serialVersionUID = 1L; + + /** the validator that produce the message */ + protected SimpleBeanValidator<?> validator; + + /** the field that produce the message */ + protected String field; + + /** the label of the message (to be displayed somewhere) */ + protected String message; + + /** the scope of the message */ + protected NuitonValidatorScope scope; + + public SimpleBeanValidatorMessage(SimpleBeanValidator<?> validator, + String field, + String message, + NuitonValidatorScope scope) { + this.field = field; + this.validator = validator; + this.message = message == null ? null : message.trim(); + this.scope = scope; + } + + public SimpleBeanValidator<?> getValidator() { + return validator; + } + + public String getField() { + return field; + } + + public NuitonValidatorScope getScope() { + return scope; + } + + public String getMessage() { + return message; + } + + @Override + public int compareTo(E o) { + // sort on scope + int result = getScope().compareTo(o.getScope()); + if (result == 0) { + // sort on field name + result = field.compareTo(o.field); + if (result == 0) { + // sort on message + result = message.compareTo(o.message); + } + } + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SimpleBeanValidatorMessage<?>)) { + return false; + } + + SimpleBeanValidatorMessage<?> that = (SimpleBeanValidatorMessage<?>) o; + + return field.equals(that.field) && + (message != null ? !message.equals(that.message) : that.message == null) && + scope == that.scope; + } + + @Override + public int hashCode() { + int result = field.hashCode(); + result = 31 * result + (message != null ? message.hashCode() : 0); + result = 31 * result + (scope != null ? scope.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return scope + " - " + getI18nError(message); + } + + public String getI18nError(String error) { + String text; + if (!error.contains("##")) { + text = _(error); + } else { + StringTokenizer stk = new StringTokenizer(error, "##"); + String errorName = stk.nextToken(); + List<String> args = new ArrayList<String>(); + while (stk.hasMoreTokens()) { + args.add(stk.nextToken()); + } + text = _(errorName, args.toArray()); + } + return text; + } +} Copied: trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/package-info.java (from rev 2376, trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/package-info.java) =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/package-info.java (rev 0) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/bean/simple/package-info.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,70 @@ +/* + * #%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 of Nuiton - Simple Bean Validator api. + * + * <h1>The <b>SimpleBeanValidator</b> api</h1> + * <p> + * The {@link org.nuiton.validator.bean.simple.SimpleBeanValidator} 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> + * SimpleBeanValidatorListener listener = new SimpleBeanValidatorListener() {XXX}; + * SimpleBeanValidator<O> validator = XXX; + * validator.addSimpleBeanValidatorListener(listener); + * validator.setBean(o); + * </pre> + * + * <h2>Obtain a validator</h2> + * To obtain a bean validator use the factory of validators + * {@link org.nuiton.validator.bean.SimpleBeanValidatorFactory}. + * <br/> + * <pre> + * SimpleBeanValidator<O> validator = SimpleBeanValidatorFactory.newValidator(O.class); + * </pre> + * + * <h2>Using the {@link org.nuiton.validator.bean.simple.SimpleBeanValidatorFactory.BeanValidatorCreator}</h2> + * + * It is possible to use a {@link org.nuiton.validator.bean.BeanValidatorFactory.BeanValidatorCreator} to change the + * implementation of {@link org.nuiton.validator.bean.BeanValidator} + * instanticated by the factory. + * + * For this just use the method + * {@link org.nuiton.validator.bean.BeanValidatorFactory#setCreator(BeanValidatorCreator)}. + * + * By default it will us a default creator + * ({@link org.nuiton.validator.bean.BeanValidatorFactory.DefaultBeanValidatorCreator}). + * + * <strong>To be continued...</strong> + * + * @since 2.0 + */ +package org.nuiton.validator.bean.simple; \ No newline at end of file Modified: trunk/nuiton-validator/src/main/java/org/nuiton/validator/xwork2/XWork2NuitonValidatorProvider.java =================================================================== --- trunk/nuiton-validator/src/main/java/org/nuiton/validator/xwork2/XWork2NuitonValidatorProvider.java 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/xwork2/XWork2NuitonValidatorProvider.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -57,7 +57,7 @@ public static final String PROVIDER_NAME = "xwork2"; /** Logger. */ - static private final Log log = + private static final Log log = LogFactory.getLog(XWork2NuitonValidatorProvider.class); public XWork2NuitonValidatorProvider() { 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 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/src/main/java/org/nuiton/validator/xwork2/XWork2ValidatorUtil.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -54,7 +54,7 @@ public class XWork2ValidatorUtil { /** Logger. */ - static private final Log log = LogFactory.getLog(XWork2ValidatorUtil.class); + private static final Log log = LogFactory.getLog(XWork2ValidatorUtil.class); /** * a shared value stack to allow external operations on it (for example add 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 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/BeanValidatorTest.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -37,7 +37,11 @@ import java.util.Arrays; import java.util.List; -/** @author tchemit <chemit@codelutin.com> */ +/** + * @author tchemit <chemit@codelutin.com> + * @deprecated since 2.5.2. + */ +@Deprecated public class BeanValidatorTest { /** Logger */ Added: trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/list/BeanListValidatorTest.java =================================================================== --- trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/list/BeanListValidatorTest.java (rev 0) +++ trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/list/BeanListValidatorTest.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,504 @@ +package org.nuiton.validator.bean.list; + +import com.google.common.collect.ArrayListMultimap; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.nuiton.validator.NuitonValidatorScope; +import org.nuiton.validator.bean.SimpleBean; + +import java.util.Arrays; +import java.util.List; + +/** + * To test the {@link BeanListValidator}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public class BeanListValidatorTest { + + /** Logger */ + private static final Log log = + LogFactory.getLog(BeanListValidatorTest.class); + + protected BeanListValidator<SimpleBean> validator; + + protected SimpleBean bean; + + protected SimpleBean bean2; + + BeanValidatorListenerImpl fatalListener; + + BeanValidatorListenerImpl errorListener; + + BeanValidatorListenerImpl warningListener; + + BeanValidatorListenerImpl infoListener; + + @Before + public void setUp() { + + bean = new SimpleBean(); + bean2 = new SimpleBean(); + } + + protected void prepareValidator(String context) { + + validator = BeanListValidator.newValidator(SimpleBean.class, context); + + validator.addBeanListValidatorListener(fatalListener = new BeanValidatorListenerImpl(NuitonValidatorScope.FATAL)); + validator.addBeanListValidatorListener(errorListener = new BeanValidatorListenerImpl(NuitonValidatorScope.ERROR)); + validator.addBeanListValidatorListener(warningListener = new BeanValidatorListenerImpl(NuitonValidatorScope.WARNING)); + validator.addBeanListValidatorListener(infoListener = new BeanValidatorListenerImpl(NuitonValidatorScope.INFO)); + } + + @After + public void tearDown() { + bean = null; + bean2 = null; + if (validator != null) { + validator.removeAllBeans(); + validator = null; + } + } + + private static final String STRING_VALUE_FATAL = "stringValue.fatal"; + + private static final String STRING_VALUE_ERROR = "stringValue.error"; + + private static final String STRING_VALUE_WARNING = "stringValue.warning"; + + private static final String INT_VALUE_FATAL = "intValue.fatal"; + + private static final String INT_VALUE_ERROR = "intValue.error"; + + 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 addBean() { + prepareValidator(null); + + assertMessages(fatalListener, bean); + assertMessages(errorListener, bean); + assertMessages(warningListener, bean); + assertMessages(infoListener, bean); + + validator.addBean(bean); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + validator.addBean(bean2); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + assertMessages(fatalListener, bean2, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean2, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean2, STRING_VALUE_WARNING); + assertMessages(infoListener, bean2, INT_VALUE_INFO); + + } + + @Test + public void addAllBean() { + prepareValidator(null); + + assertMessages(fatalListener, bean); + assertMessages(errorListener, bean); + assertMessages(warningListener, bean); + assertMessages(infoListener, bean); + + assertMessages(fatalListener, bean2); + assertMessages(errorListener, bean2); + assertMessages(warningListener, bean2); + assertMessages(infoListener, bean2); + + validator.addAllBeans(Arrays.asList(bean, bean2)); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + assertMessages(fatalListener, bean2, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean2, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean2, STRING_VALUE_WARNING); + assertMessages(infoListener, bean2, INT_VALUE_INFO); + + } + @Test + public void removeBean() { + prepareValidator(null); + + assertMessages(fatalListener, bean); + assertMessages(errorListener, bean); + assertMessages(warningListener, bean); + assertMessages(infoListener, bean); + + assertMessages(fatalListener, bean2); + assertMessages(errorListener, bean2); + assertMessages(warningListener, bean2); + assertMessages(infoListener, bean2); + + validator.addBean(bean); + validator.addBean(bean2); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + assertMessages(fatalListener, bean2, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean2, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean2, STRING_VALUE_WARNING); + assertMessages(infoListener, bean2, INT_VALUE_INFO); + + validator.removeBean(bean); + + assertMessages(fatalListener, bean); + assertMessages(errorListener, bean); + assertMessages(warningListener, bean); + assertMessages(infoListener, bean); + + validator.removeBean(bean2); + + assertMessages(fatalListener, bean2); + assertMessages(errorListener, bean2); + assertMessages(warningListener, bean2); + assertMessages(infoListener, bean2); + } + + @Test + public void removeAllBeans() { + prepareValidator(null); + + assertMessages(fatalListener, bean); + assertMessages(errorListener, bean); + assertMessages(warningListener, bean); + assertMessages(infoListener, bean); + + assertMessages(fatalListener, bean2); + assertMessages(errorListener, bean2); + assertMessages(warningListener, bean2); + assertMessages(infoListener, bean2); + + validator.addBean(bean); + validator.addBean(bean2); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + assertMessages(fatalListener, bean2, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean2, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean2, STRING_VALUE_WARNING); + assertMessages(infoListener, bean2, INT_VALUE_INFO); + + validator.removeAllBeans(Arrays.asList(bean, bean2)); + + assertMessages(fatalListener, bean); + assertMessages(errorListener, bean); + assertMessages(warningListener, bean); + assertMessages(infoListener, bean); + + assertMessages(fatalListener, bean2); + assertMessages(errorListener, bean2); + assertMessages(warningListener, bean2); + assertMessages(infoListener, bean2); + } + + @Test + public void removeAllBeans2() { + prepareValidator(null); + + assertMessages(fatalListener, bean); + assertMessages(errorListener, bean); + assertMessages(warningListener, bean); + assertMessages(infoListener, bean); + + assertMessages(fatalListener, bean2); + assertMessages(errorListener, bean2); + assertMessages(warningListener, bean2); + assertMessages(infoListener, bean2); + + validator.addBean(bean); + validator.addBean(bean2); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + assertMessages(fatalListener, bean2, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean2, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean2, STRING_VALUE_WARNING); + assertMessages(infoListener, bean2, INT_VALUE_INFO); + + validator.removeAllBeans(); + + assertMessages(fatalListener, bean); + assertMessages(errorListener, bean); + assertMessages(warningListener, bean); + assertMessages(infoListener, bean); + + assertMessages(fatalListener, bean2); + assertMessages(errorListener, bean2); + assertMessages(warningListener, bean2); + assertMessages(infoListener, bean2); + } + + @Test + public void validate() { + + prepareValidator(null); + + assertMessages(fatalListener, bean); + assertMessages(errorListener, bean); + assertMessages(warningListener, bean); + assertMessages(infoListener, bean); + + if (log.isDebugEnabled()) { + log.debug("- bean 1 ----------------------------------------------"); + } + validator.addBean(bean); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("- bean 1 ----------------------------------------------"); + } + bean.setStringValue("one"); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, INT_VALUE_ERROR); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("- bean 1 ----------------------------------------------"); + } + bean.setStringValue("oneone"); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, INT_VALUE_ERROR); + assertMessages(warningListener, bean); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("- bean 1 ----------------------------------------------"); + } + bean.setIntValue(1); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean); + assertMessages(warningListener, bean); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("- bean 1 ----------------------------------------------"); + } + bean.setIntValue(10); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean); + assertMessages(warningListener, bean); + assertMessages(infoListener, bean); + + if (log.isDebugEnabled()) { + log.debug("- bean 1 ----------------------------------------------"); + } + + bean.setStringValue(null); + bean.setIntValue(0); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + + if (log.isDebugEnabled()) { + log.debug("- bean 1 ----------------------------------------------"); + } + + bean.setStringValue("5"); + bean.setIntValue(5); + assertMessages(fatalListener, bean); + assertMessages(errorListener, bean); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("- bean 2 ----------------------------------------------"); + } + validator.addBean(bean2); + + assertMessages(fatalListener, bean2, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean2, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean2, STRING_VALUE_WARNING); + assertMessages(infoListener, bean2, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("- bean 2 ----------------------------------------------"); + } + bean2.setStringValue("one"); + + assertMessages(fatalListener, bean2, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean2, INT_VALUE_ERROR); + assertMessages(warningListener, bean2, STRING_VALUE_WARNING); + assertMessages(infoListener, bean2, INT_VALUE_INFO); + + } + + @Test + public void convert() { + + prepareValidator(null); + + assertMessages(errorListener, bean); + assertMessages(warningListener, bean); + assertMessages(infoListener, bean); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + + validator.addBean(bean); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + + Object value = validator.convert(bean, "intValue", "abc", Class.class); + + Assert.assertNull(value); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, STRING_VALUE_ERROR, "error.convertor.class"); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + bean.setStringValue("one"); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, "error.convertor.class"); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + + value = validator.convert(bean, "intValue", "3", Integer.class); + + bean.setIntValue((Integer) value); + + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + + bean.setIntValue(-1); + assertMessages(fatalListener, bean, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, bean, INT_VALUE_ERROR); + assertMessages(warningListener, bean, STRING_VALUE_WARNING); + assertMessages(infoListener, bean, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + } + + void assertMessages(BeanValidatorListenerImpl listener, + Object bean, + String... expected) { + List<String> actual = listener.getMessages(bean); + Assert.assertEquals(" shoudl have " + + Arrays.toString(expected) + " but had " + actual, + expected.length, actual.size()); + for (String m : expected) { + Assert.assertEquals("could not find " + m + " in " + actual, + true, actual.contains(m)); + } + } + + class BeanValidatorListenerImpl implements BeanListValidatorListener { + + private final NuitonValidatorScope scope; + + private final ArrayListMultimap<Object, String> messages; + + public BeanValidatorListenerImpl(NuitonValidatorScope scope) { + this.scope = scope; + messages = ArrayListMultimap.create(); + } + + + public ArrayListMultimap<Object, String> getMessages() { + return messages; + } + + public List<String> getMessages(Object bean) { + return messages.get(bean); + } + + + @Override + public void onFieldChanged(BeanListValidatorEvent event) { + if (scope != event.getScope()) { + return; + } + Object bean = event.getBean(); + String[] messagesToDelete = event.getMessagesToDelete(); + if (messagesToDelete != null && messagesToDelete.length > 0) { + if (log.isDebugEnabled()) { + log.debug(scope + "[" + bean + "] messages to delete : " + Arrays.toString(messagesToDelete)); + } + for (String m : messagesToDelete) { + messages.remove(bean, m); + } + } + String[] messagesToAdd = event.getMessagesToAdd(); + if (messagesToAdd != null && messagesToAdd.length > 0) { + if (log.isDebugEnabled()) { + log.debug(scope + "[" + bean + "] messages to add : " + Arrays.toString(messagesToAdd)); + } + messages.putAll(bean, Arrays.asList(messagesToAdd)); + } + } + } +} \ No newline at end of file Copied: trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorTest.java (from rev 2376, trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/BeanValidatorTest.java) =================================================================== --- trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorTest.java (rev 0) +++ trunk/nuiton-validator/src/test/java/org/nuiton/validator/bean/simple/SimpleBeanValidatorTest.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -0,0 +1,314 @@ +/* + * #%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.simple; + +import com.google.common.collect.Lists; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.nuiton.validator.NuitonValidatorScope; +import org.nuiton.validator.bean.SimpleBean; + +import java.util.Arrays; +import java.util.List; + +/** + * To test the {@link SimpleBeanValidator}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.2 + */ +public class SimpleBeanValidatorTest { + + /** Logger */ + private static final Log log = + LogFactory.getLog(SimpleBeanValidatorTest.class); + + protected SimpleBeanValidator<SimpleBean> validator; + + protected SimpleBean bean; + + ValidatorListenerImpl fatalListener; + + ValidatorListenerImpl errorListener; + + ValidatorListenerImpl warningListener; + + ValidatorListenerImpl infoListener; + + @Before + public void setUp() { + + bean = new SimpleBean(); + } + + protected void prepareValidator(String context) { + + validator = SimpleBeanValidator.newValidator(SimpleBean.class, context); + + validator.addSimpleBeanValidatorListener(fatalListener = new ValidatorListenerImpl(NuitonValidatorScope.FATAL)); + validator.addSimpleBeanValidatorListener(errorListener = new ValidatorListenerImpl(NuitonValidatorScope.ERROR)); + validator.addSimpleBeanValidatorListener(warningListener = new ValidatorListenerImpl(NuitonValidatorScope.WARNING)); + validator.addSimpleBeanValidatorListener(infoListener = new ValidatorListenerImpl(NuitonValidatorScope.INFO)); + } + + @After + public void tearDown() { + bean = null; + if (validator != null) { + validator.setBean(null); + validator = null; + } + } + + private static final String STRING_VALUE_FATAL = "stringValue.fatal"; + + private static final String STRING_VALUE_ERROR = "stringValue.error"; + + private static final String STRING_VALUE_WARNING = "stringValue.warning"; + + private static final String INT_VALUE_FATAL = "intValue.fatal"; + + private static final String INT_VALUE_ERROR = "intValue.error"; + + 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 validate() { + + prepareValidator(null); + + assertMessages(fatalListener); + assertMessages(errorListener); + assertMessages(warningListener); + assertMessages(infoListener); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + validator.setBean(bean); + + assertMessages(fatalListener, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, STRING_VALUE_WARNING); + assertMessages(infoListener, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + bean.setStringValue("one"); + + assertMessages(fatalListener, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, INT_VALUE_ERROR); + assertMessages(warningListener, STRING_VALUE_WARNING); + assertMessages(infoListener, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + bean.setStringValue("oneone"); + + assertMessages(fatalListener, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, INT_VALUE_ERROR); + assertMessages(warningListener); + assertMessages(infoListener, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + bean.setIntValue(1); + + assertMessages(fatalListener, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener); + assertMessages(warningListener); + assertMessages(infoListener, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + bean.setIntValue(10); + + assertMessages(fatalListener, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener); + assertMessages(warningListener); + assertMessages(infoListener); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + + bean.setStringValue(null); + bean.setIntValue(0); + + assertMessages(fatalListener, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, STRING_VALUE_WARNING); + assertMessages(infoListener, INT_VALUE_INFO); + + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + + bean.setStringValue("5"); + bean.setIntValue(5); + assertMessages(fatalListener); + assertMessages(errorListener); + assertMessages(warningListener, STRING_VALUE_WARNING); + assertMessages(infoListener, INT_VALUE_INFO); + } + + @Test + public void convert() { + + prepareValidator(null); + + assertMessages(errorListener); + assertMessages(warningListener); + assertMessages(infoListener); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + + validator.setBean(bean); + + assertMessages(fatalListener, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, STRING_VALUE_ERROR, INT_VALUE_ERROR); + assertMessages(warningListener, STRING_VALUE_WARNING); + assertMessages(infoListener, INT_VALUE_INFO); + + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + + Object value = validator.convert("intValue", "abc", Class.class); + + Assert.assertNull(value); + + assertMessages(fatalListener, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, STRING_VALUE_ERROR, "error.convertor.class"); + assertMessages(warningListener, STRING_VALUE_WARNING); + assertMessages(infoListener, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + bean.setStringValue("one"); + + assertMessages(fatalListener, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, "error.convertor.class"); + assertMessages(warningListener, STRING_VALUE_WARNING); + assertMessages(infoListener, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + + value = validator.convert("intValue", "3", Integer.class); + + bean.setIntValue((Integer) value); + + assertMessages(fatalListener, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener); + assertMessages(warningListener, STRING_VALUE_WARNING); + assertMessages(infoListener, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + + bean.setIntValue(-1); + assertMessages(fatalListener, STRING_VALUE_FATAL, INT_VALUE_FATAL); + assertMessages(errorListener, INT_VALUE_ERROR); + assertMessages(warningListener, STRING_VALUE_WARNING); + assertMessages(infoListener, INT_VALUE_INFO); + + if (log.isDebugEnabled()) { + log.debug("-----------------------------------------------"); + } + } + + void assertMessages(ValidatorListenerImpl listener, + String... expected) { + List<String> actual = listener.getMessages(); + Assert.assertEquals(" shoudl have " + + Arrays.toString(expected) + " but had " + actual, + expected.length, actual.size()); + for (String m : expected) { + Assert.assertEquals("could not find " + m + " in " + actual, + true, actual.contains(m)); + } + } + + static class ValidatorListenerImpl implements SimpleBeanValidatorListener { + + final NuitonValidatorScope scope; + + public ValidatorListenerImpl(NuitonValidatorScope scope) { + this.scope = scope; + } + + List<String> messages = Lists.newArrayList(); + + public List<String> getMessages() { + return messages; + } + + @Override + public void onFieldChanged(SimpleBeanValidatorEvent event) { + if (scope == event.getScope()) { + String[] messagesToDelete = event.getMessagesToDelete(); + if (messagesToDelete != null && messagesToDelete.length > 0) { + if (log.isDebugEnabled()) { + log.debug(event.getScope() + " messages to delete : " + Arrays.toString(messagesToDelete)); + } + for (String m : messagesToDelete) { + messages.remove(m); + } + } + String[] messagesToAdd = event.getMessagesToAdd(); + if (messagesToAdd != null && messagesToAdd.length > 0) { + if (log.isDebugEnabled()) { + log.debug(event.getScope() + " messages to add : " + Arrays.toString(messagesToAdd)); + } + messages.addAll(Arrays.asList(messagesToAdd)); + } + } + } + } +} Modified: trunk/nuiton-validator/src/test/java/org/nuiton/validator/xwork2/XWork2NuitonValidatorProviderTest.java =================================================================== --- trunk/nuiton-validator/src/test/java/org/nuiton/validator/xwork2/XWork2NuitonValidatorProviderTest.java 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/src/test/java/org/nuiton/validator/xwork2/XWork2NuitonValidatorProviderTest.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -53,7 +53,7 @@ public class XWork2NuitonValidatorProviderTest { /** Logger. */ - static private final Log log = + private static final Log log = LogFactory.getLog(XWork2NuitonValidatorProviderTest.class); protected XWork2NuitonValidatorProvider provider; Modified: trunk/nuiton-validator/src/test/java/org/nuiton/validator/xwork2/field/AbstractFieldValidatorTest.java =================================================================== --- trunk/nuiton-validator/src/test/java/org/nuiton/validator/xwork2/field/AbstractFieldValidatorTest.java 2012-07-25 10:25:46 UTC (rev 2376) +++ trunk/nuiton-validator/src/test/java/org/nuiton/validator/xwork2/field/AbstractFieldValidatorTest.java 2012-07-27 14:16:19 UTC (rev 2377) @@ -50,7 +50,7 @@ public abstract class AbstractFieldValidatorTest<B> extends Assert { /** Logger */ - static private final Log log = + private static final Log log = LogFactory.getLog(AbstractFieldValidatorTest.class); protected static NuitonValidator<?> cacheValidator;
participants (1)
-
tchemit@users.nuiton.org