View Javadoc
1   package org.nuiton.validator.bean;
2   /*
3    * #%L
4    * Nuiton Validator
5    * %%
6    * Copyright (C) 2013 - 2014 Code Lutin, Tony Chemit
7    * %%
8    * This program is free software: you can redistribute it and/or modify
9    * it under the terms of the GNU Lesser General Public License as 
10   * published by the Free Software Foundation, either version 3 of the 
11   * License, or (at your option) any later version.
12   * 
13   * This program is distributed in the hope that it will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   * GNU General Lesser Public License for more details.
17   * 
18   * You should have received a copy of the GNU General Lesser Public 
19   * License along with this program.  If not, see
20   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
21   * #L%
22   */
23  
24  import com.google.common.base.Preconditions;
25  import org.apache.commons.lang3.ObjectUtils;
26  import org.nuiton.util.beans.BeanUtil;
27  import org.nuiton.validator.NuitonValidator;
28  import org.nuiton.validator.NuitonValidatorModel;
29  import org.nuiton.validator.NuitonValidatorProvider;
30  import org.nuiton.validator.NuitonValidatorScope;
31  
32  import javax.swing.event.EventListenerList;
33  import java.beans.PropertyChangeEvent;
34  import java.beans.PropertyChangeListener;
35  import java.beans.PropertyChangeSupport;
36  import java.util.Set;
37  
38  /**
39   * TODO
40   *
41   * @param <O> type of bean to validate
42   * @author Tony Chemit - chemit@codelutin.com
43   * @since 2.5.2
44   */
45  public abstract class AbstractValidator<O> {
46  
47      /**
48       * Name of the bounded property {@code context}.
49       *
50       * @see #getContext()
51       * @see #setContext(String)
52       */
53      public static final String CONTEXT_PROPERTY = "context";
54  
55      /**
56       * Name of the bounded property {@code scopes}.
57       *
58       * @see #getScopes()
59       * @see #setScopes(NuitonValidatorScope...)
60       */
61      public static final String SCOPES_PROPERTY = "scopes";
62  
63      /**
64       * Name of the bounded property {@link #valid}.
65       *
66       * @see #valid
67       * @see #isValid()
68       * @see #setValid(boolean)
69       */
70      public static final String VALID_PROPERTY = "valid";
71  
72      /**
73       * Name of the bounded property {@link #changed}.
74       *
75       * @see #changed
76       * @see #isChanged()
77       * @see #setChanged(boolean)
78       */
79      public static final String CHANGED_PROPERTY = "changed";
80  
81      /**
82       * State to indicate that validator has changed since the last time bean was
83       * setted.
84       */
85      protected boolean changed;
86  
87      /** State of the validator (is true if no errors of error scope is found). */
88      protected boolean valid = true;
89  
90      /**
91       * State to know if the validator can be used (we keep this state for
92       * performance reasons : do not want to compute this value each time a
93       * validation is asked...).
94       */
95      protected boolean canValidate = true;
96  
97      /** Listener that listens on bean modification. */
98      protected final PropertyChangeListener l;
99  
100     /** delegate property change support */
101     protected final PropertyChangeSupport pcs;
102 
103     /** A list of event listeners for this validators */
104     protected final EventListenerList listenerList = new EventListenerList();
105 
106     /**
107      * The provider of delegate validators.
108      *
109      * It will also produce validator model.
110      *
111      * @see NuitonValidatorProvider
112      */
113     protected final NuitonValidatorProvider validatorProvider;
114 
115 
116     protected AbstractValidator(NuitonValidatorProvider validatorProvider,
117                                 Class<O> beanClass) {
118 
119         // check if given bean class is Javabean compiliant
120         boolean javaBeanCompiliant = BeanUtil.isJavaBeanCompiliant(beanClass);
121         Preconditions.checkState(
122                 javaBeanCompiliant,
123                 beanClass.getName() + " is not JavaBean compiliant (" +
124                 BeanUtil.ADD_PROPERTY_CHANGE_LISTENER + ", or " +
125                 BeanUtil.REMOVE_PROPERTY_CHANGE_LISTENER +
126                 " method not found).");
127 
128         this.validatorProvider = validatorProvider;
129 
130         pcs = new PropertyChangeSupport(this);
131 
132         l = new PropertyChangeListener() {
133 
134             @Override
135             public void propertyChange(PropertyChangeEvent evt) {
136 
137                 O bean = (O) evt.getSource();
138 
139                 // the bean has changed, replay validation
140                 doValidate(bean);
141             }
142         };
143     }
144 
145     /**
146      * Obtain the {@link #changed} property value.
147      *
148      * Returns {@code true} if bean was modified since last
149      * time a bean was attached.
150      *
151      * @return {@code true} if bean was modified since last attachement of
152      * a bean.
153      */
154     public boolean isChanged() {
155         return changed;
156     }
157 
158     /**
159      * To force the value of the property {@link #changed}.
160      *
161      * @param changed flag to force reset of property {@link #changed}
162      */
163     public void setChanged(boolean changed) {
164         this.changed = changed;
165 
166         // force the property to be fired (never pass the older value)
167         firePropertyChange(CHANGED_PROPERTY, null, changed);
168     }
169 
170     public boolean isCanValidate() {
171         return canValidate;
172     }
173 
174     public void setCanValidate(boolean canValidate) {
175         this.canValidate = canValidate;
176     }
177 
178     /**
179      * Obtain the {@link #valid} property value.
180      *
181      * @return {@code true} if attached bean is valid (no error or fatal messages)
182      */
183     public boolean isValid() {
184         return valid;
185     }
186 
187     /**
188      * Change the value of the {@link #valid} property.
189      *
190      * @param valid the new value of the property
191      */
192     public void setValid(boolean valid) {
193         this.valid = valid;
194 
195         // force the property to be fired (never pass the older value)
196         firePropertyChange(VALID_PROPERTY, null, valid);
197     }
198 
199 
200     public String getContext() {
201         return getModel().getContext();
202     }
203 
204     public void setContext(String context) {
205 
206         String oldContext = getContext();
207 
208         if (ObjectUtils.equals(context, oldContext)) {
209 
210             // same context do nothing
211             return;
212         }
213 
214         NuitonValidatorModel<O> model = getModel();
215 
216         // compute the new validator model
217         NuitonValidatorScope[] scopes = model.getScopes().toArray(
218                 new NuitonValidatorScope[model.getScopes().size()]);
219 
220         rebuildDelegateValidator(
221                 model.getType(),
222                 context,
223                 scopes
224         );
225 
226         firePropertyChange(CONTEXT_PROPERTY,
227                            oldContext,
228                            context
229         );
230     }
231 
232     public Set<NuitonValidatorScope> getScopes() {
233         return getModel().getScopes();
234     }
235 
236     public Set<NuitonValidatorScope> getEffectiveScopes() {
237         return getDelegate().getEffectiveScopes();
238     }
239 
240     public Set<String> getEffectiveFields() {
241         return getDelegate().getEffectiveFields();
242     }
243 
244     public Set<String> getEffectiveFields(NuitonValidatorScope scope) {
245         return getDelegate().getEffectiveFields(scope);
246     }
247 
248     public void setScopes(NuitonValidatorScope... scopes) {
249 
250         Set<NuitonValidatorScope> oldScopes = getScopes();
251 
252         rebuildDelegateValidator(
253                 getModel().getType(),
254                 getModel().getContext(),
255                 scopes
256         );
257 
258         firePropertyChange(SCOPES_PROPERTY,
259                            oldScopes,
260                            scopes
261         );
262     }
263 
264     public abstract void doValidate();
265 
266     public abstract boolean hasFatalErrors();
267 
268     public abstract boolean hasErrors();
269 
270     public abstract boolean hasWarnings();
271 
272     public abstract boolean hasInfos();
273 
274     public abstract boolean isValid(String fieldName);
275 
276     public abstract NuitonValidatorScope getHighestScope(String field);
277 
278     public abstract <T> T convert(O bean, String fieldName, String value, Class<T> valueClass);
279 
280     protected abstract void doValidate(O bean);
281 
282     protected abstract NuitonValidator<O> getDelegate();
283 
284     protected abstract void rebuildDelegateValidator(Class<O> beanType,
285                                                      String context,
286                                                      NuitonValidatorScope... scopes);
287 
288     public Class<O> getType() {
289         return getModel().getType();
290     }
291 
292     /**
293      * Test a the validator contains the field given his name
294      *
295      * @param fieldName the name of the searched field
296      * @return <code>true</code> if validator contaisn this field,
297      * <code>false</code> otherwise
298      */
299     public boolean containsField(String fieldName) {
300         Set<String> effectiveFields = getDelegate().getEffectiveFields();
301         boolean result = effectiveFields.contains(fieldName);
302         return result;
303     }
304 
305     public void addPropertyChangeListener(PropertyChangeListener listener) {
306         pcs.addPropertyChangeListener(listener);
307     }
308 
309     public void addPropertyChangeListener(String propertyName,
310                                           PropertyChangeListener listener) {
311         pcs.addPropertyChangeListener(propertyName, listener);
312     }
313 
314     public void removePropertyChangeListener(PropertyChangeListener listener) {
315         pcs.removePropertyChangeListener(listener);
316     }
317 
318     public void removePropertyChangeListener(String propertyName,
319                                              PropertyChangeListener listener) {
320         pcs.removePropertyChangeListener(propertyName, listener);
321     }
322 
323     public void firePropertyChange(String propertyName,
324                                    Object oldValue,
325                                    Object newValue) {
326         pcs.firePropertyChange(propertyName, oldValue, newValue);
327     }
328 
329     protected NuitonValidatorModel<O> getModel() {
330         return getDelegate().getModel();
331     }
332 }