View Javadoc
1   /*
2    * #%L
3    * Nuiton Validator
4    * %%
5    * Copyright (C) 2013 - 2014 Code Lutin, Tony Chemit
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as 
9    * published by the Free Software Foundation, either version 3 of the 
10   * License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Lesser Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Lesser Public 
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
20   * #L%
21   */
22  package org.nuiton.validator.xwork2;
23  
24  import com.opensymphony.xwork2.ActionContext;
25  import com.opensymphony.xwork2.DefaultLocaleProvider;
26  import com.opensymphony.xwork2.ValidationAwareSupport;
27  import com.opensymphony.xwork2.util.ValueStack;
28  import com.opensymphony.xwork2.validator.ActionValidatorManager;
29  import com.opensymphony.xwork2.validator.DelegatingValidatorContext;
30  import com.opensymphony.xwork2.validator.ValidationException;
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  
34  import java.util.ArrayList;
35  import java.util.Collection;
36  import java.util.Collections;
37  import java.util.HashMap;
38  import java.util.List;
39  import java.util.Map;
40  import java.util.Set;
41  
42  /**
43   * A customized validator for a given bean.
44   *
45   * Use the method {@link #validate(Object)} to obtain the messages detected by
46   * the validator for the given bean.
47   *
48   * @param <O> type of the bean to validate.
49   * @author Tony Chemit - chemit@codelutin.com
50   * @since 2.0
51   */
52  public class XWork2ScopeValidator<O> {
53  
54      /** Logger */
55      private static final Log log = LogFactory.getLog(XWork2ScopeValidator.class);
56  
57      protected final static Map<String, List<String>> EMPTY_RESULT =
58              Collections.unmodifiableMap(new HashMap<String, List<String>>());
59  
60      /** the type of bean to validate */
61      protected final Class<O> type;
62  
63      /** the validation named context (can be null) */
64      protected String context;
65  
66      /** the list of field names detected for this validator */
67      protected Set<String> fieldNames;
68  
69      // --
70      // XWorks fields
71      // --
72  
73      protected ValidationAwareSupport validationSupport;
74  
75      protected DelegatingValidatorContext validationContext;
76  
77      protected ActionValidatorManager validator;
78  
79      protected ValueStack vs;
80  
81      protected XWork2ScopeValidator(Class<O> type,
82                                     String context,
83                                     Set<String> fieldNames,
84                                     ValueStack vs) {
85  
86          this.type = type;
87          this.context = context;
88          this.fieldNames = fieldNames;
89  
90          validationSupport = new ValidationAwareSupport();
91          validationContext = new DelegatingValidatorContext(validationSupport, null, new DefaultLocaleProvider());
92  
93          if (vs == null) {
94  
95              // create a standalone value stack
96              vs = XWork2ValidatorUtil.createValuestack();
97              if (log.isDebugEnabled()) {
98                  log.debug("create a standalone value stack " + vs);
99              }
100         } else {
101             if (log.isDebugEnabled()) {
102                 log.debug("use given value stack " + vs);
103             }
104         }
105 
106         this.vs = vs;
107 
108         validator = XWork2ValidatorUtil.newValidationManager(vs);
109     }
110 
111     public Class<O> getType() {
112         return type;
113     }
114 
115     public String getContext() {
116         return context;
117     }
118 
119     public Set<String> getFieldNames() {
120         return fieldNames;
121     }
122 
123     public ActionValidatorManager getValidator() {
124         return validator;
125     }
126 
127     /**
128      * Test if the validator contains the field given his name
129      *
130      * @param fieldName the name of the searched field
131      * @return <code>true</code> if validator contaisn this field,
132      * <code>false</code> otherwise
133      */
134     public boolean containsField(String fieldName) {
135         return fieldNames.contains(fieldName);
136     }
137 
138     /**
139      * Valide le bean donné et retourne les messages produits.
140      *
141      * @param bean le bean a valider (il doit etre non null)
142      * @return le dictionnaire des messages produits par la validation indexées
143      * par le nom du champs du bean impacté.
144      */
145     public Map<String, List<String>> validate(O bean) {
146 
147         if (bean == null) {
148             throw new NullPointerException(
149                     "bean parameter can not be null in method validate");
150         }
151 
152         Map<String, List<String>> result = EMPTY_RESULT;
153 
154 
155         if (fieldNames.isEmpty()) {
156             return result;
157         }
158 
159         // on lance la validation uniquement si des champs sont a valider
160         try {
161 
162             //TC - 20081024 : since context is in a ThreadLocal variable,
163             // we must do the check
164             if (ActionContext.getContext() == null) {
165                 ActionContext.setContext(new ActionContext(vs.getContext()));
166             }
167 
168             validator.validate(bean, context, validationContext);
169 
170             if (log.isTraceEnabled()) {
171                 log.trace("Action errors: " +
172                           validationContext.getActionErrors());
173                 log.trace("Action messages: " +
174                           validationContext.getActionMessages());
175                 log.trace("Field errors: " +
176                           validationContext.getFieldErrors());
177             }
178 
179             if (log.isDebugEnabled()) {
180                 log.debug(this + " : " +
181                           validationContext.getFieldErrors());
182             }
183 
184             // retreave errors by field
185             if (validationContext.hasFieldErrors()) {
186                 Map<?, ?> messages = validationContext.getFieldErrors();
187                 result = new HashMap<String, List<String>>(messages.size());
188                 for (Object fieldName : messages.keySet()) {
189                     Collection<?> c =
190                             (Collection<?>) messages.get(fieldName);
191                     List<String> mm = new ArrayList<String>(c.size());
192                     for (Object message : c) {
193                         // tchemit 2010-08-28 : trim the incoming message
194                         // (I18n will not translate it otherwise)
195                         String messageStr = message == null ? "" : message + "";
196                         mm.add(messageStr.trim());
197                     }
198                     result.put(fieldName + "", mm);
199                 }
200             }
201 
202         } catch (ValidationException eee) {
203             if (log.isWarnEnabled()) {
204                 log.warn("Error during validation on " + type +
205                          " for reason : " + eee.getMessage(), eee);
206             }
207 
208         } finally {
209 
210             // on nettoye toujours le validateur apres operation
211             validationSupport.clearErrorsAndMessages();
212         }
213 
214         return result;
215     }
216 
217     @Override
218     public String toString() {
219         return super.toString() + "<beanClass:" + type +
220                ", contextName:" + context + ">";
221     }
222 
223 }