View Javadoc

1   /*
2    * $Id: DefaultValidatorDelegate.java,v 1.9 2004/04/09 18:44:53 eelco12 Exp $
3    * $Revision: 1.9 $
4    * $Date: 2004/04/09 18:44:53 $
5    *
6    * ====================================================================
7    * Copyright (c) 2003, Open Edge B.V.
8    * All rights reserved.
9    * Redistribution and use in source and binary forms, with or without 
10   * modification, are permitted provided that the following conditions are met:
11   * Redistributions of source code must retain the above copyright notice, 
12   * this list of conditions and the following disclaimer. Redistributions 
13   * in binary form must reproduce the above copyright notice, this list of 
14   * conditions and the following disclaimer in the documentation and/or other 
15   * materials provided with the distribution. Neither the name of OpenEdge B.V. 
16   * nor the names of its contributors may be used to endorse or promote products 
17   * derived from this software without specific prior written permission.
18   * 
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
20   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
21   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
22   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
23   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
24   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
25   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
26   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
27   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
28   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
29   * THE POSSIBILITY OF SUCH DAMAGE.
30   */
31   
32  package nl.openedge.baritus;
33  
34  import java.util.ArrayList;
35  import java.util.Collection;
36  import java.util.Iterator;
37  import java.util.List;
38  import java.util.Map;
39  
40  import nl.openedge.baritus.util.MultiHashMap;
41  import nl.openedge.baritus.validation.FieldValidator;
42  import nl.openedge.baritus.validation.FormValidator;
43  import nl.openedge.baritus.validation.ValidationRuleDependend;
44  import nl.openedge.baritus.validation.ValidationActivationRule;
45  
46  import ognl.Ognl;
47  
48  import org.apache.commons.logging.Log;
49  import org.apache.commons.logging.LogFactory;
50  import org.infohazard.maverick.flow.ControllerContext;
51  
52  /***
53   * @author Eelco Hillenius
54   */
55  public final class DefaultValidatorDelegate implements ValidatorDelegate
56  {
57  	// validator registry
58  	private ValidatorRegistry validatorRegistry = null;
59  	
60  	// instance of formbean ctrl
61  	private FormBeanCtrlBase ctrl = null;
62  	
63  	/* population log */
64  	private static Log populationLog = LogFactory.getLog(LogConstants.POPULATION_LOG);
65  	
66  	private static char[] BREAKSYMBOLS = new char[]{'[','('};
67  	
68  	/***
69  	 * construct with validator registry and instance of ctrl
70  	 * @param validatorRegistry
71  	 * @param ctrl
72  	 */
73  	public DefaultValidatorDelegate(
74  		ValidatorRegistry validatorRegistry,
75  		FormBeanCtrlBase ctrl)
76  	{
77  		this.validatorRegistry = validatorRegistry;
78  		this.ctrl = ctrl;
79  	}
80  	
81  	/***
82  	 * @see nl.openedge.baritus.ValidatorDelegate#doValidation(org.infohazard.maverick.flow.ControllerContext, nl.openedge.baritus.FormBeanContext, nl.openedge.baritus.ExecutionParams, java.util.Map, boolean)
83  	 */
84  	public boolean doValidation(
85  		ControllerContext cctx, 
86  		FormBeanContext formBeanContext, 
87  		ExecutionParams execParams,
88  		Map parameters,
89  		boolean succeeded)
90  	{
91  		
92  		if(parameters == null) return succeeded;
93  		
94  		MultiHashMap fieldValidators = validatorRegistry.getFieldValidators();
95  		List formValidators = validatorRegistry.getFormValidators();
96  		List globalValidatorActivationRules = 
97  			validatorRegistry.getGlobalValidatorActivationRules();
98  		
99  		if( (fieldValidators != null && (!fieldValidators.isEmpty())) ||
100 			(formValidators != null && (!formValidators.isEmpty())))
101 		{
102 	
103 			boolean doCustomValidation = true;
104 			// see if there's any globally (form level) defined rules
105 			if(globalValidatorActivationRules != null && (!globalValidatorActivationRules.isEmpty()))
106 			{
107 				for(Iterator i = globalValidatorActivationRules.iterator(); i.hasNext(); )
108 				{
109 					ValidationActivationRule rule = (ValidationActivationRule)i.next();
110 					doCustomValidation = rule.allowValidation(cctx, formBeanContext); // fire rule
111 					if(!doCustomValidation) break;
112 				}
113 			}
114 			
115 			if(doCustomValidation)
116 			{
117 				// if fieldValidators were registered
118 				if(fieldValidators != null && (!fieldValidators.isEmpty()))
119 				{
120 					Iterator names = parameters.keySet().iterator(); // loop through the properties
121 					while(names.hasNext())
122 					{
123 						String name = (String)names.next();
124 						if (name == null) continue;
125 						if(formBeanContext.getOverrideField(name) == null) 
126 							// see if there allready was an override registered
127 						{
128 							succeeded = doValidationForOneField(
129 								fieldValidators, cctx, formBeanContext, succeeded, name);	
130 						} // else an error allready occured; do not validate
131 					}	
132 				}
133 				// if we are still successful so far, check with the form level validators
134 				if( (succeeded || execParams.isDoFormValidationIfFieldValidationFailed()) 
135 					&& (formValidators != null))
136 				{
137 					// check all registered until either all fired successfully or
138 					// one did not fire succesfully
139 					for(Iterator i = formValidators.iterator(); i.hasNext(); )
140 					{
141 						FormValidator fValidator = (FormValidator)i.next();
142 						succeeded = doFormValidationForOneValidator(
143 							cctx, formBeanContext, fValidator, succeeded);
144 					}
145 				}
146 			}
147 		}
148 		
149 		return succeeded;
150 	}
151 	
152 	/* execute validation for one form validator */
153 	private boolean doFormValidationForOneValidator(
154 		ControllerContext cctx,
155 		FormBeanContext formBeanContext,
156 		FormValidator fValidator,
157 		boolean succeeded)
158 	{
159 		
160 		boolean success = true;
161 		try
162 		{
163 			boolean validateForm = true;
164 			if(fValidator instanceof ValidationRuleDependend)
165 			{
166 				ValidationActivationRule fRule = 
167 					((ValidationRuleDependend)fValidator).getValidationActivationRule();
168 				if(fRule != null)
169 				{
170 					if(!fRule.allowValidation(cctx, formBeanContext))
171 					{
172 						validateForm = false;
173 					}
174 					
175 					if(populationLog.isDebugEnabled())
176 					{
177 						populationLog.debug("rule " + fRule + 
178 							((validateForm) ? " ALLOWS" : " DISALLOWS") +
179 							" validation with " + fValidator);
180 					}	
181 				}
182 			}
183 			if(validateForm)
184 			{
185 				success = fValidator.isValid(cctx, formBeanContext);
186 				
187 				if(populationLog.isDebugEnabled())
188 				{
189 					populationLog.debug( "validation" +
190 						((success) ? " PASSED" : " FAILED") +
191 						" using validator " + fValidator);
192 				}	
193 			}
194 		} 
195 		catch (Exception e)
196 		{
197 			success = false;
198 			String msg = "validator " + fValidator + " threw exception: " + e.getMessage();
199 			populationLog.error(msg);
200 			populationLog.error(e.getMessage(), e);	
201 		}
202 		
203 		if(!success) succeeded = false;
204 		
205 		return succeeded;
206 	}
207 	
208 	/* execute validation for one field */
209 	private boolean doValidationForOneField(
210 		MultiHashMap fieldValidators,
211 		ControllerContext cctx,
212 		FormBeanContext formBeanContext,
213 		boolean succeeded,
214 		String name)
215 	{
216 
217 		Collection propertyValidators = 
218 			getFieldValidatorsForField(name, fieldValidators);
219 		// these are the fieldValidators for one property
220 		
221 		if(propertyValidators != null)
222 		{
223 			try
224 			{
225 				succeeded = doValidationForOneField(
226 					cctx, formBeanContext, succeeded, 
227 					name, propertyValidators);
228 			}
229 			catch (Exception e)
230 			{
231 				succeeded = false;
232 				populationLog.error(e.getMessage(), e);
233 			}
234 		}
235 		
236 		return succeeded;
237 	}
238 	
239 	/* Get the validators for a field, possibly null. */
240 	private List getFieldValidatorsForField(
241 		String name, MultiHashMap fieldValidators)
242 	{
243 		List propertyValidators = null;
244 		propertyValidators = getFieldValidatorsForFieldRecursively(
245 			name, fieldValidators, propertyValidators);
246 		return propertyValidators;	
247 	}
248 	
249 	/* 
250 	 * Get the validators for a field, null if none found.
251 	 * work our way back to simple property name
252 	 * e.g., take complex (bogus) case 'myproperty('key1')[1]('key2')[2]',
253 	 * we should be able to look for registered validators with:
254 	 * 	- myproperty['key1'][1]['key2'][2]
255 	 * 	- myproperty['key1'][1]['key2']
256 	 * 	- myproperty['key1'][1]
257 	 * 	- myproperty['key1']
258 	 * 	- myproperty 
259 	 */
260 	private List getFieldValidatorsForFieldRecursively(
261 		String currentName, MultiHashMap fieldValidators, List propertyValidators)
262 	{
263 		List validators = (List)fieldValidators.get(currentName);
264 		if(validators != null)
265 		{
266 			if(propertyValidators == null) propertyValidators = new ArrayList();
267 			propertyValidators.addAll(validators);
268 		}
269 		
270 		int delim = 0;
271 		for(int i = 0; i < BREAKSYMBOLS.length; i++)
272 		{
273 			int ix = currentName.lastIndexOf(BREAKSYMBOLS[i]);
274 			if(ix > -1)
275 			{
276 				delim = ix;
277 				break;
278 			}	
279 		}
280 		
281 		if(delim > 0)
282 		{
283 			// just cut off wihout further checking
284 			currentName = currentName.substring(0, delim);
285 			propertyValidators = getFieldValidatorsForFieldRecursively(
286 				currentName, fieldValidators, propertyValidators);
287 		}
288 		
289 		return propertyValidators;
290 	}
291 	
292 	/* handle the custom validation for one field */
293 	private boolean doValidationForOneField(
294 		ControllerContext cctx,
295 		FormBeanContext formBeanContext,
296 		boolean succeeded,
297 		String name,
298 		Collection propertyValidators)
299 		throws Exception
300 	{
301 		// get target value;
302 		// this could be done a bit more efficient, as we allready had
303 		// the (converted) value when populating. Working more efficient 
304 		// (like with a converted value cache) would make the API of
305 		// populators less straightforward, and by getting the property
306 		// from the bean instead of using the converted value, we are
307 		// sure that we get the property the proper (java beans) way.
308 		Object value = Ognl.getValue(name, formBeanContext.getBean());
309 		
310 		// for all validators for this field
311 		for(Iterator j = propertyValidators.iterator(); j.hasNext(); )
312 		{
313 			FieldValidator validator = (FieldValidator)j.next();
314 			boolean validateField = true;
315 								
316 			if(validator instanceof ValidationRuleDependend) // should we execute rule
317 			{
318 				ValidationActivationRule rule = 
319 					((ValidationRuleDependend)validator)
320 						.getValidationActivationRule();
321 				if(rule != null)
322 				{
323 					validateField = rule.allowValidation(cctx, formBeanContext); //test
324 					
325 					if(populationLog.isDebugEnabled())
326 					{
327 						populationLog.debug( name + ": rule " + rule + 
328 							((validateField) ? " ALLOWS" : " DISALLOWS") +
329 							" validation with " + validator);
330 					}
331 				}
332 			}
333 								
334 			if(validateField)
335 			{
336 				// execute validation method
337 				boolean success;
338 				try
339 				{
340 					success = validator.isValid(cctx, formBeanContext, name, value);
341 				}
342 				catch (Exception e)
343 				{
344 					String msg = "validator " + validator + " threw exception: " +
345 						e.getMessage() + " on property " + name + " with value " +
346 						value;
347 					populationLog.error(msg);
348 					throw e;
349 				}
350 				
351 				if(populationLog.isDebugEnabled())
352 				{
353 					populationLog.debug( "validation" +
354 						((success) ? " PASSED" : " FAILED") +
355 						" for field " + name + " using validator " + validator);
356 				}
357 				
358 				if(!success)
359 				{
360 					succeeded = false;
361 					ctrl.setOverrideField(
362 						cctx, formBeanContext, name, value, null, validator);
363 					break;
364 				}	
365 			}
366 		}
367 		return succeeded;	
368 	}
369 	
370 	/***
371 	 * @return char[]
372 	 */
373 	public static char[] getBREAKSYMBOLS()
374 	{
375 		return BREAKSYMBOLS;
376 	}
377 
378 	/***
379 	 * @param cs
380 	 */
381 	public static void setBREAKSYMBOLS(char[] cs)
382 	{
383 		BREAKSYMBOLS = cs;
384 	}
385 
386 }