Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ShaleValidatorAction |
|
| 3.7777777777777777;3.778 |
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.util; | |
19 | ||
20 | import java.lang.reflect.Method; | |
21 | import java.lang.reflect.Modifier; | |
22 | import java.util.ArrayList; | |
23 | import java.util.HashMap; | |
24 | import java.util.List; | |
25 | import java.util.Locale; | |
26 | import java.util.Map; | |
27 | import org.apache.commons.validator.Arg; | |
28 | import org.apache.commons.validator.Field; | |
29 | import org.apache.commons.validator.Form; | |
30 | import org.apache.commons.validator.ValidatorAction; | |
31 | import org.apache.commons.validator.ValidatorResources; | |
32 | ||
33 | /** | |
34 | * <p>Custom wrapper around a Commons Validator <code>ValidatorAction</code> | |
35 | * that precalculates as many of the introspective lookup operations as | |
36 | * possible. This ensures that runtime operation of the validator checks | |
37 | * can proceed as quickly as possible.</p> | |
38 | */ | |
39 | public final class ShaleValidatorAction { | |
40 | ||
41 | ||
42 | // ------------------------------------------------------------ Constructors | |
43 | ||
44 | ||
45 | /** | |
46 | * <p>Create a new instance of ShaleValidatorAction that wraps the | |
47 | * specified <code>ValidatorAction</code>.</p> | |
48 | * | |
49 | * @param resources <code>ValidatorResources</code> for this application | |
50 | * @param action The <code>ValidatorAction</code> to be wrapped | |
51 | * | |
52 | * @exception IllegalArgumentException if configuration data is missing | |
53 | * or incorrect | |
54 | */ | |
55 | public ShaleValidatorAction(ValidatorResources resources, | |
56 | 1623 | ValidatorAction action) { |
57 | ||
58 | 1623 | this.resources = resources; |
59 | 1623 | this.action = action; |
60 | 1623 | precalculate(); |
61 | ||
62 | 1623 | } |
63 | ||
64 | ||
65 | // -------------------------------------------------------- Static Variables | |
66 | ||
67 | ||
68 | /** | |
69 | * <p>The prefix for form names that correspond to validator types.</p> | |
70 | */ | |
71 | private static final String FORM_NAME_PREFIX = "org.apache.shale.validator."; | |
72 | ||
73 | ||
74 | /** | |
75 | * <p>Map of the classes for primitive Java types, keyed by the Java | |
76 | * keywords for the corresponding primitive.</p> | |
77 | */ | |
78 | 1 | private static final Map PRIMITIVE_TYPES = new HashMap(); |
79 | static { | |
80 | 1 | PRIMITIVE_TYPES.put("boolean", boolean.class); |
81 | 1 | PRIMITIVE_TYPES.put("byte", byte.class); |
82 | 1 | PRIMITIVE_TYPES.put("char", char.class); |
83 | 1 | PRIMITIVE_TYPES.put("double", double.class); |
84 | 1 | PRIMITIVE_TYPES.put("float", float.class); |
85 | 1 | PRIMITIVE_TYPES.put("int", int.class); |
86 | 1 | PRIMITIVE_TYPES.put("long", long.class); |
87 | 1 | PRIMITIVE_TYPES.put("short", short.class); |
88 | 1 | } |
89 | ||
90 | ||
91 | // ------------------------------------------------------ Instance Variables | |
92 | ||
93 | ||
94 | /** | |
95 | * <p>The <code>ValidatorAction</code> that we are wrapping.</p> | |
96 | */ | |
97 | 1623 | private ValidatorAction action = null; |
98 | ||
99 | ||
100 | /** | |
101 | * <p>The class containing the validation method to be called for this action.</p> | |
102 | */ | |
103 | 1623 | private Class clazz = null; |
104 | ||
105 | ||
106 | /** | |
107 | * <p>The field definition containing the mappings for parameter and | |
108 | * message subtitution for this validator type.</p> | |
109 | */ | |
110 | 1623 | private Field field = null; |
111 | ||
112 | ||
113 | /** | |
114 | * <p>The form definition corresponding to this validator type that is used | |
115 | * for mapping parameter and message arguments.</p> | |
116 | */ | |
117 | 1623 | private Form form = null; |
118 | ||
119 | ||
120 | /** | |
121 | * <p>Return an instance of the specified validation class, if the requested | |
122 | * validation method is not static.</p> | |
123 | */ | |
124 | 1623 | private Object instance = null; |
125 | ||
126 | ||
127 | /** | |
128 | * <p>Array of message arguments defining replacement parameters for error | |
129 | * messages related to this validator.</p> | |
130 | */ | |
131 | 1623 | private Arg[] messageArgs = null; |
132 | ||
133 | ||
134 | /** | |
135 | * <p>The validation <code>Method</code> to be called for this action.</p> | |
136 | */ | |
137 | 1623 | private Method method = null; |
138 | ||
139 | ||
140 | /** | |
141 | * <p>Array of parameter arguments defining values to be sent in to | |
142 | * the validator method for this validator.</p> | |
143 | */ | |
144 | 1623 | private Arg[] parameterArgs = null; |
145 | ||
146 | ||
147 | /** | |
148 | * <p>The <code>ValidatorResources</code> for this application.</p> | |
149 | */ | |
150 | 1623 | private ValidatorResources resources = null; |
151 | ||
152 | ||
153 | /** | |
154 | * <p>The parameter signature for the validation method to be called | |
155 | * for this action.</p> | |
156 | */ | |
157 | 1623 | private Class[] signature = null; |
158 | ||
159 | ||
160 | // -------------------------------------------------------------- Properties | |
161 | ||
162 | ||
163 | /** | |
164 | * <p>Return the <code>ValidatorAction</code> instance we are wrapping.</p> | |
165 | */ | |
166 | public ValidatorAction getAction() { | |
167 | 1623 | return this.action; |
168 | } | |
169 | ||
170 | ||
171 | /** | |
172 | * <p>Return an instance of the requested validator class, if the requested | |
173 | * validation method is not static. If the method is static, return | |
174 | * <code>null</code> instead.</p> | |
175 | */ | |
176 | public Object getInstance() { | |
177 | return this.instance; | |
178 | } | |
179 | ||
180 | ||
181 | /** | |
182 | * <p>Return an array of argument metadata describing subtitution values | |
183 | * for error messages emitted by this validator type.</p> | |
184 | */ | |
185 | public Arg[] getMessageArgs() { | |
186 | return this.messageArgs; | |
187 | } | |
188 | ||
189 | ||
190 | /** | |
191 | * <p>Return the lookup key for the error message template to be used | |
192 | * if this validation fails.</p> | |
193 | */ | |
194 | public String getMessageKey() { | |
195 | return this.action.getMsg(); | |
196 | } | |
197 | ||
198 | ||
199 | /** | |
200 | * <p>Return the validation <code>Method</code> to be called for this | |
201 | * action.</p> | |
202 | */ | |
203 | public Method getMethod() { | |
204 | return this.method; | |
205 | } | |
206 | ||
207 | ||
208 | /** | |
209 | * <p>Return an array of argument metadata describing the parameter values | |
210 | * to be sent to the validation method for this validator type.</p> | |
211 | */ | |
212 | public Arg[] getParameterArgs() { | |
213 | return this.parameterArgs; | |
214 | } | |
215 | ||
216 | ||
217 | /** | |
218 | * <p>Return the parameter signature for the validation method to be called | |
219 | * for this action.</p> | |
220 | */ | |
221 | public Class[] getSignature() { | |
222 | return this.signature; | |
223 | } | |
224 | ||
225 | ||
226 | // --------------------------------------------------------- Private Methods | |
227 | ||
228 | ||
229 | /** | |
230 | * <p>Precalculate the values to be returned by our public properties.</p> | |
231 | * | |
232 | * @exception IllegalArgumentException if configuration data is missing | |
233 | * or incorrect | |
234 | */ | |
235 | private void precalculate() { | |
236 | ||
237 | 1623 | List list = null; |
238 | 1623 | String value = null; |
239 | 1623 | String values = null; |
240 | ||
241 | // Acquire a reference to the class loader we will use | |
242 | 1623 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
243 | 1623 | if (loader == null) { |
244 | loader = this.getClass().getClassLoader(); | |
245 | } | |
246 | ||
247 | // Look up the class that contains the validation method | |
248 | // we will be calling | |
249 | try { | |
250 | 1623 | this.clazz = loader.loadClass(action.getClassname()); |
251 | } catch (ClassNotFoundException e) { | |
252 | throw new IllegalArgumentException("ClassNotFoundException:" | |
253 | + " Cannot load class '" + action.getClassname() | |
254 | + "' specified by ValidatorAction with name '" | |
255 | + action.getName() + "'"); | |
256 | 1623 | } |
257 | ||
258 | // Calculate the method parameter signature of the validation | |
259 | // method we will be calling | |
260 | 1623 | list = new ArrayList(); |
261 | 1623 | values = action.getMethodParams().trim(); |
262 | 4197 | while (values.length() > 0) { |
263 | // Identify the class name of the next class to be loaded | |
264 | 2574 | int comma = values.indexOf(','); |
265 | 2574 | if (comma >= 0) { |
266 | 951 | value = values.substring(0, comma).trim(); |
267 | 951 | values = values.substring(comma + 1); |
268 | 951 | } else { |
269 | 1623 | value = values.trim(); |
270 | 1623 | values = ""; |
271 | } | |
272 | 2574 | if (value.length() == 0) { |
273 | break; | |
274 | } | |
275 | // Add the corresponding class instance to our list | |
276 | 2574 | Class clazz = (Class) PRIMITIVE_TYPES.get(value); |
277 | 2574 | if (clazz == null) { |
278 | try { | |
279 | 1551 | clazz = loader.loadClass(value); |
280 | } catch (ClassNotFoundException e) { | |
281 | throw new IllegalArgumentException("ClassNotFoundException:" | |
282 | + " Cannot load method parameter class '" + value | |
283 | + "' specified by ValidatorAction with name '" | |
284 | + action.getName() + "'"); | |
285 | 1551 | } |
286 | } | |
287 | 2574 | list.add(clazz); |
288 | 2574 | } |
289 | 1623 | this.signature = (Class[]) list.toArray(new Class[list.size()]); |
290 | ||
291 | // Look up the validation method we will be calling | |
292 | try { | |
293 | 1623 | this.method = this.clazz.getMethod(action.getMethod(), this.signature); |
294 | } catch (NoSuchMethodException e) { | |
295 | throw new IllegalArgumentException("NoSuchMethodException:" | |
296 | + " Method named '" + action.getMethod() + "' with parameters '" | |
297 | + action.getMethodParams() + "' for class '" + action.getClassname() | |
298 | + "' not found. Specified by ValidatorAction with name '" | |
299 | + action.getName() + "'"); | |
300 | 1623 | } |
301 | ||
302 | // Create an instance of the validator class if we need one | |
303 | // (which is true only if the validation method is not static | |
304 | 1623 | if (!Modifier.isStatic(this.method.getModifiers())) { |
305 | try { | |
306 | this.instance = clazz.newInstance(); | |
307 | } catch (IllegalAccessException e) { | |
308 | throw new IllegalArgumentException("IllegalAccessException:" | |
309 | + " Not allowed to instantiate class '" + action.getClassname() | |
310 | + "' specified by ValidatorAction with name '" + action.getName() | |
311 | + "' (validation method with name '" + action.getMethod() | |
312 | + "' is not static)"); | |
313 | } catch (InstantiationException e) { | |
314 | throw new IllegalArgumentException("InstantiationException:" | |
315 | + " Could not instantiate class '" + action.getClassname() | |
316 | + "' specified by ValidatorAction with name '" + action.getName() | |
317 | + "' (validation method with name '" + action.getMethod() | |
318 | + "' is not static)"); | |
319 | } | |
320 | } | |
321 | ||
322 | // Cache the form definition associated with this validator action type | |
323 | 1623 | this.form = resources.getForm(Locale.getDefault(), |
324 | FORM_NAME_PREFIX + action.getName()); | |
325 | 1623 | if (this.form == null) { |
326 | throw new IllegalArgumentException(FORM_NAME_PREFIX + action.getName()); | |
327 | } | |
328 | ||
329 | // Look up the predefined "fields" from our form definition | |
330 | // and cache interesting information | |
331 | 1623 | this.field = this.form.getField(action.getName()); |
332 | 1623 | if (this.field == null) { |
333 | throw new IllegalArgumentException("Field " + action.getName()); | |
334 | } | |
335 | 1623 | this.messageArgs = field.getArgs("message"); |
336 | 1623 | if (this.messageArgs == null) { |
337 | this.messageArgs = new Arg[0]; | |
338 | } | |
339 | 1623 | this.parameterArgs = field.getArgs("parameter"); |
340 | 1623 | if (this.parameterArgs == null) { |
341 | this.parameterArgs = new Arg[0]; | |
342 | } | |
343 | ||
344 | // FIXME - For some reason, Commons Validator is returning a trailing | |
345 | // null Arg in the parameterArgs array sometimes, so strip it off | |
346 | // if present | |
347 | 1623 | if ((this.parameterArgs.length > 0) |
348 | && (this.parameterArgs[this.parameterArgs.length - 1] == null)) { | |
349 | 1623 | Arg[] results = new Arg[this.parameterArgs.length - 1]; |
350 | 1623 | System.arraycopy(this.parameterArgs, 0, results, 0, results.length); |
351 | 1623 | this.parameterArgs = results; |
352 | } | |
353 | ||
354 | // For robustness, validate the length of the parameter arguments | |
355 | // and parameter signature arrays, which should be identical | |
356 | 1623 | if (this.parameterArgs.length != this.signature.length) { |
357 | throw new IllegalArgumentException(this.action.getName() | |
358 | + ": signature defines " + this.signature.length | |
359 | + " elements but " + this.parameterArgs.length | |
360 | + " parameter arguments are specified"); | |
361 | } | |
362 | ||
363 | 1623 | } |
364 | ||
365 | ||
366 | } |