2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shale.validator.validator;
19  
20  import java.lang.reflect.InvocationTargetException;
21  import java.lang.reflect.Method;
22  import java.text.MessageFormat;
23  import java.util.Locale;
24  import java.util.Map;
25  
26  import javax.faces.application.FacesMessage;
27  import javax.faces.component.UIComponent;
28  import javax.faces.context.FacesContext;
29  import javax.faces.validator.Validator;
30  import javax.faces.validator.ValidatorException;
31  
32  import org.apache.commons.validator.Arg;
33  import org.apache.commons.validator.ValidatorResources;
34  import org.apache.shale.util.ConverterHelper;
35  import org.apache.shale.validator.Globals;
36  import org.apache.shale.validator.util.AbstractUtilities;
37  import org.apache.shale.validator.util.ShaleValidatorAction;
38  
39  /***
40   * <p>Abstract base class for validators that use Apache Commons Validator
41   * as their foundation.</p>
42   */
43  public abstract class AbstractValidator extends AbstractUtilities
44    implements Validator {
45      
46  
47      // ------------------------------------------------------------ Constructors
48  
49  
50      // ------------------------------------------------------ Manifest Constants
51  
52  
53      /***
54       * <p>Variable name in a <code>vars</code> map representing the argument
55       * that was being evaluated, and should be included in any error message.
56       * Typically, this will be either the value itself or a user-friendly
57       * (localized) field label.</p>
58       */
59      protected static final String ARG_VALUE =
60              "arg";
61  
62  
63      /***
64       * <p>Variable name in a <code>vars</code> map representing the maximum
65       * value that should be accepted by this <code>Validator</code>.</p>
66       */
67      protected static final String MAXIMUM_VALUE =
68              "max";
69  
70  
71      /***
72       * <p>Variable name in a <code>vars</code> map representing the minimum
73       * value that should be accepted by this <code>Validator</code>.</p>
74       */
75      protected static final String MINIMUM_VALUE =
76              "min";
77  
78  
79      /***
80       * <p>Variable name in a <code>vars</code> map representing the submitted
81       * value that should be validated by this <code>Validator</code>.</p>
82       */
83      protected static final String SUBMITTED_VALUE =
84              "submittedValue";
85  
86  
87      // -------------------------------------------------------- Static Variables
88  
89  
90      /***
91       * <p>Converter helper instance we can use in the <code>convert()</code>
92       * method implementation.</p>
93       */
94      protected static final ConverterHelper helper = new ConverterHelper();
95  
96  
97  
98      // -------------------------------------------------------------- Properties
99  
100 
101     private boolean client = true;
102 
103 
104     /***
105      * <p>Return a flag describing whether this validator should be enforced
106      * on the client side or not.  Default is <code>true</code>.</p>
107      */
108     public boolean isClient() {
109         return this.client;
110     }
111 
112 
113     /***
114      * <p>Set a flag describing whether this validator should be enforced
115      * on the client side or not.</p>
116      *
117      * @param client The new client enforcement flag
118      */
119     public void setClient(boolean client) {
120         this.client = client;
121     }
122 
123 
124     // ------------------------------------------------------- Validator Methods
125 
126 
127     /***
128      * <p>Perform the correctness checks implemented by this
129      * {@link Validator} against the specified {@link UIComponent}.
130      * If any violations are found, a {@link ValidatorException}
131      * will be thrown containing the {@link javax.faces.application.FacesMessage}
132      * describing the failure.</p>
133      *
134      * <p><strong>IMPLEMENTATION NOTE</strong>:  Unlike earlier implementations
135      * of Shale Validator integration, validators that subclass this class do
136      * not support the option to skip server side validation.</p>
137      *
138      * @param context <code>FacesContext</code> for the current request
139      * @param component <code>UIComponent</code> we are checking for correctness
140      * @param value The value to validate
141      *
142      * @throws ValidatorException if validation fails
143      * @throws NullPointerException if <code>context</code>
144      *  or <code>component</code> is <code>null</code>
145      */
146     public abstract void validate(FacesContext context,
147                                   UIComponent  component,
148                                   Object       value) throws ValidatorException;
149 
150 
151     // ----------------------------------------------------- StateHolder Methods
152 
153 
154     /*** {@inheritDoc} */
155     public void restoreState(FacesContext context, Object state) {
156         Object[] values = (Object[]) state;
157         super.restoreState(context, values[0]);
158         this.client = Boolean.TRUE.equals((Boolean) values[1]);
159     }
160 
161 
162     /*** {@inheritDoc} */
163     public Object saveState(FacesContext context) {
164         Object[] values = new Object[2];
165         values[0] = super.saveState(context);
166         values[1] = this.client ? Boolean.TRUE : Boolean.FALSE;
167         return values;
168     }
169 
170 
171     // ---------------------------------------------------------- Object Methods
172 
173 
174     // ------------------------------------------------------- Protected Methods
175 
176 
177     /***
178      * <p>Return an array of <code>ShaleValidatorAction</code>s to execute
179      * for a given validation, starting with the configured dependent
180      * actions, and ending with the action corresponding to the specified
181      * action type.  If there is no defined action with the specified
182      * type, return <code>null</code>.</p>
183      *
184      * @param context <code>FacesContext</code> for the current request
185      * @param type Type of the validator action for which to return actions
186      */
187     protected ShaleValidatorAction[] actions(FacesContext context, String type) {
188 
189         Map actions = (Map) context.getExternalContext().
190           getApplicationMap().get(Globals.VALIDATOR_ACTIONS);
191         return (ShaleValidatorAction[]) actions.get(type);
192 
193     }
194 
195 
196     /***
197      * <p>Use the registered converters to convert the specified value
198      * to the specified type.</p>
199      *
200      * @param context <code>FacesContext</code> for the current request
201      * @param value Value to be converted
202      * @param type Type to which the value should be converted
203      */
204     protected Object convert(FacesContext context, Object value, Class type) {
205 
206         // Is the specified value null?  If so, return it unchanged
207         if (value == null) {
208             return null;
209         }
210 
211         // Is the specified value of the correct type already?
212         // If so, return it unchanged
213         if (type.isInstance(value)) {
214             return value;
215         }
216 
217         // Is the target type String?  If so, use the asString() conversion
218         if (type == String.class) {
219             if (value instanceof String) {
220                 return value;
221             } else {
222                 return helper.asString(context, value.getClass(), value);
223             }
224         }
225 
226         // Is the source type String?  If so, use the asObject() conversion
227         if (value instanceof String) {
228             return helper.asObject(context, type, (String) value);
229         }
230 
231         // Fall back to converting to String, then to the requested type
232         String string = helper.asString(context, value.getClass(), value);
233         return helper.asObject(context, type, string);
234 
235     }
236 
237 
238     /***
239      * <p>Return the <code>ValidatorResources</code> that describe the
240      * validation rules to be enforced by this application.</p>
241      *
242      * @param context <code>FacesContext</code> for the current request
243      */
244     protected ValidatorResources resources(FacesContext context) {
245 
246         return (ValidatorResources) context.getExternalContext().
247           getApplicationMap().get(Globals.VALIDATOR_RESOURCES);
248 
249     }
250 
251 
252     /***
253      * <p>Perform a validation using the specified Commons Validator type.</p>
254      *
255      * @param context <code>FacesContext</code> for the current request
256      * @param component <code>UIComponent</code> whose value is being validated
257      * @param value The value being validated
258      * @param type Type of validation to be performed
259      * @param vars Mutable map of values to be passed in to the validation
260      *
261      * @exception ValidatorException if a validation error occurs
262      */
263     protected void validate(FacesContext context, UIComponent component,
264                             Object value, String type, Map vars)
265       throws ValidatorException {
266 
267         // Look up the actions we must perform
268         ShaleValidatorAction[] actions = actions(context, type);
269         if (actions == null) {
270             throw new IllegalArgumentException("No validator for type '"
271                                                + type + "' has been configured");
272         }
273 
274         // Perform the actions in order, throwing a ValidatorException
275         // on the first one that fails (per the standard Validator contract)
276         for (int i = 0; i < actions.length; i++) {
277             Object instance = actions[i].getInstance();
278             Method method = actions[i].getMethod();
279             Class[] signature = actions[i].getSignature();
280             Arg[] args = actions[i].getParameterArgs();
281             Object[] parameters = new Object[signature.length];
282             for (int j = 0; j < parameters.length; j++) {
283                 parameters[j] = convert(context, vars.get(args[j].getKey()), signature[j]);
284             }
285             Boolean result = null;
286             try {
287                 result = (Boolean) method.invoke(instance, parameters);
288             } catch (IllegalAccessException e) {
289                 ;
290             } catch (InvocationTargetException e) {
291                 ;
292             }
293             if (!result.booleanValue()) {
294                 String error = getMessage();
295                 if (error == null) {
296                     error = message(context, actions[i].getMessageKey());
297                 }
298                 args = actions[i].getMessageArgs();
299                 parameters = new Object[args.length];
300                 for (int j = 0; j < parameters.length; j++) {
301                     parameters[j] = vars.get(args[j].getKey());
302                 }
303                 Locale locale = context.getViewRoot().getLocale();
304                 String formatted = new MessageFormat(error, locale).format(parameters);
305                 FacesMessage message =
306                   new FacesMessage(FacesMessage.SEVERITY_ERROR, formatted, null);
307                 throw new ValidatorException(message);
308             }
309         }
310 
311     }
312     
313 
314 }