View Javadoc
1   /*******************************************************************************
2    * Portions created by Sebastian Thomschke are copyright (c) 2005-2015 Sebastian
3    * Thomschke.
4    *
5    * All Rights Reserved. This program and the accompanying materials
6    * are made available under the terms of the Eclipse Public License v1.0
7    * which accompanies this distribution, and is available at
8    * http://www.eclipse.org/legal/epl-v10.html
9    *
10   * Contributors:
11   *     Sebastian Thomschke - initial implementation.
12   *******************************************************************************/
13  package net.sf.oval;
14  
15  import static java.lang.Boolean.*;
16  
17  import java.lang.reflect.Constructor;
18  import java.lang.reflect.Field;
19  import java.lang.reflect.Method;
20  import java.util.Collection;
21  import java.util.LinkedHashSet;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Set;
25  import java.util.WeakHashMap;
26  
27  import net.sf.oval.collection.CollectionFactory;
28  import net.sf.oval.collection.CollectionFactoryJDKImpl;
29  import net.sf.oval.collection.CollectionFactoryJavolutionImpl;
30  import net.sf.oval.collection.CollectionFactoryTroveImpl;
31  import net.sf.oval.configuration.Configurer;
32  import net.sf.oval.configuration.annotation.AnnotationsConfigurer;
33  import net.sf.oval.configuration.annotation.JPAAnnotationsConfigurer;
34  import net.sf.oval.configuration.pojo.POJOConfigurer;
35  import net.sf.oval.configuration.pojo.elements.ClassConfiguration;
36  import net.sf.oval.configuration.pojo.elements.ConstraintSetConfiguration;
37  import net.sf.oval.configuration.pojo.elements.ConstructorConfiguration;
38  import net.sf.oval.configuration.pojo.elements.FieldConfiguration;
39  import net.sf.oval.configuration.pojo.elements.MethodConfiguration;
40  import net.sf.oval.configuration.pojo.elements.ObjectConfiguration;
41  import net.sf.oval.configuration.pojo.elements.ParameterConfiguration;
42  import net.sf.oval.configuration.xml.XMLConfigurer;
43  import net.sf.oval.constraint.AssertConstraintSetCheck;
44  import net.sf.oval.constraint.AssertFieldConstraintsCheck;
45  import net.sf.oval.constraint.AssertValidCheck;
46  import net.sf.oval.constraint.ConstraintsCheck;
47  import net.sf.oval.constraint.NotNullCheck;
48  import net.sf.oval.context.ConstructorParameterContext;
49  import net.sf.oval.context.FieldContext;
50  import net.sf.oval.context.MethodParameterContext;
51  import net.sf.oval.context.MethodReturnValueContext;
52  import net.sf.oval.context.OValContext;
53  import net.sf.oval.exception.ConstraintSetAlreadyDefinedException;
54  import net.sf.oval.exception.ConstraintsViolatedException;
55  import net.sf.oval.exception.ExceptionTranslator;
56  import net.sf.oval.exception.FieldNotFoundException;
57  import net.sf.oval.exception.InvalidConfigurationException;
58  import net.sf.oval.exception.MethodNotFoundException;
59  import net.sf.oval.exception.OValException;
60  import net.sf.oval.exception.ReflectionException;
61  import net.sf.oval.exception.UndefinedConstraintSetException;
62  import net.sf.oval.exception.ValidationFailedException;
63  import net.sf.oval.expression.ExpressionLanguageRegistry;
64  import net.sf.oval.guard.ParameterNameResolver;
65  import net.sf.oval.guard.ParameterNameResolverEnumerationImpl;
66  import net.sf.oval.internal.ClassChecks;
67  import net.sf.oval.internal.ContextCache;
68  import net.sf.oval.internal.Log;
69  import net.sf.oval.internal.MessageRenderer;
70  import net.sf.oval.internal.util.ArrayUtils;
71  import net.sf.oval.internal.util.Assert;
72  import net.sf.oval.internal.util.IdentitySet;
73  import net.sf.oval.internal.util.ReflectionUtils;
74  import net.sf.oval.internal.util.StringUtils;
75  import net.sf.oval.internal.util.ThreadLocalLinkedList;
76  import net.sf.oval.localization.context.OValContextRenderer;
77  import net.sf.oval.localization.context.ToStringValidationContextRenderer;
78  import net.sf.oval.localization.locale.LocaleProvider;
79  import net.sf.oval.localization.locale.ThreadLocalLocaleProvider;
80  import net.sf.oval.localization.message.MessageResolver;
81  import net.sf.oval.localization.message.ResourceBundleMessageResolver;
82  import net.sf.oval.localization.value.MessageValueFormatter;
83  import net.sf.oval.localization.value.ToStringMessageValueFormatter;
84  import net.sf.oval.logging.LoggerFactory;
85  import net.sf.oval.ogn.ObjectGraphNavigationResult;
86  import net.sf.oval.ogn.ObjectGraphNavigatorRegistry;
87  
88  /**
89   * <p>Instances of this class can validate objects based on declared constraints.
90   * Constraints can either be declared using OVal's constraint annotations, XML configuration
91   * files or EJB3 JPA annotations.</p>
92   *
93   * <p>This class is thread-safe.</p>
94   *
95   * @author Sebastian Thomschke
96   *
97   * @see AnnotationsConfigurer
98   * @see JPAAnnotationsConfigurer
99   * @see POJOConfigurer
100  * @see XMLConfigurer
101  */
102 public class Validator implements IValidator
103 {
104 	protected static final class DelegatingParameterNameResolver implements ParameterNameResolver
105 	{
106 		private ParameterNameResolver delegate;
107 
108 		public DelegatingParameterNameResolver(final ParameterNameResolver delegate)
109 		{
110 			this.delegate = delegate;
111 		}
112 
113 		public ParameterNameResolver getDelegate()
114 		{
115 			return delegate;
116 		}
117 
118 		public String[] getParameterNames(final Constructor< ? > constructor) throws ReflectionException
119 		{
120 			return delegate.getParameterNames(constructor);
121 		}
122 
123 		public String[] getParameterNames(final Method method) throws ReflectionException
124 		{
125 			return delegate.getParameterNames(method);
126 		}
127 
128 		public void setDelegate(final ParameterNameResolver delegate)
129 		{
130 			this.delegate = delegate;
131 		}
132 	}
133 
134 	private static final Log LOG = Log.getLog(Validator.class);
135 
136 	private static CollectionFactory collectionFactory = _createDefaultCollectionFactory();
137 
138 	private static OValContextRenderer contextRenderer = ToStringValidationContextRenderer.INSTANCE;
139 
140 	private static MessageResolver messageResolver;
141 
142 	private static MessageValueFormatter messageValueFormatter = ToStringMessageValueFormatter.INSTANCE;
143 
144 	private static LocaleProvider localeProvider = new ThreadLocalLocaleProvider();
145 
146 	private static CollectionFactory _createDefaultCollectionFactory()
147 	{
148 		// if Javolution collection classes are found use them by default
149 		if (ReflectionUtils.isClassPresent("javolution.util.FastMap") && ReflectionUtils.isClassPresent("javolution.util.FastSet")
150 				&& ReflectionUtils.isClassPresent("javolution.util.FastTable"))
151 		{
152 			LOG.info("javolution.util collection classes are available.");
153 
154 			return new CollectionFactoryJavolutionImpl();
155 		}
156 		// else if Trove collection classes are found use them by default
157 		else if (ReflectionUtils.isClassPresent("gnu.trove.map.hash.THashMap")
158 				&& ReflectionUtils.isClassPresent("gnu.trove.set.hash.THashSet"))
159 		{
160 			LOG.info("gnu.trove collection classes are available.");
161 
162 			return new CollectionFactoryTroveImpl();
163 		}
164 		// else use JDK collection classes by default
165 		else
166 			return new CollectionFactoryJDKImpl();
167 	}
168 
169 	/**
170 	 * Returns a shared instance of the CollectionFactory
171 	 */
172 	public static CollectionFactory getCollectionFactory()
173 	{
174 		return collectionFactory;
175 	}
176 
177 	public static OValContextRenderer getContextRenderer()
178 	{
179 		return contextRenderer;
180 	}
181 
182 	public static LocaleProvider getLocaleProvider()
183 	{
184 		return localeProvider;
185 	}
186 
187 	public static LoggerFactory getLoggerFactory()
188 	{
189 		return Log.getLoggerFactory();
190 	}
191 
192 	public static MessageResolver getMessageResolver()
193 	{
194 		/*
195 		 * since ResourceBundleMessageResolver references getCollectionFactory() of this class
196 		 * we are lazy referencing the resolvers shared instance.
197 		 */
198 		if (messageResolver == null)
199 		{
200 			messageResolver = ResourceBundleMessageResolver.INSTANCE;
201 		}
202 		return messageResolver;
203 	}
204 
205 	public static MessageValueFormatter getMessageValueFormatter()
206 	{
207 		return messageValueFormatter;
208 	}
209 
210 	/**
211 	 *
212 	 * @param factory the new collection factory to be used by all validator instances
213 	 */
214 	public static void setCollectionFactory(final CollectionFactory factory) throws IllegalArgumentException
215 	{
216 		Assert.argumentNotNull("factory", factory);
217 		Validator.collectionFactory = factory;
218 	}
219 
220 	/**
221 	 * @param contextRenderer the contextRenderer to set
222 	 */
223 	public static void setContextRenderer(final OValContextRenderer contextRenderer)
224 	{
225 		Assert.argumentNotNull("contextRenderer", contextRenderer);
226 		Validator.contextRenderer = contextRenderer;
227 	}
228 
229 	public static void setLocaleProvider(final LocaleProvider localeProvider)
230 	{
231 		Assert.argumentNotNull("localeProvider", localeProvider);
232 		Validator.localeProvider = localeProvider;
233 	}
234 
235 	/**
236 	 * @param loggerFactory the loggerFactory to set
237 	 */
238 	public static void setLoggerFactory(final LoggerFactory loggerFactory)
239 	{
240 		Assert.argumentNotNull("loggerFactory", loggerFactory);
241 		Log.setLoggerFactory(loggerFactory);
242 	}
243 
244 	/**
245 	 * @param messageResolver the messageResolver to set
246 	 * @throws IllegalArgumentException if <code>messageResolver == null</code>
247 	 */
248 	public static void setMessageResolver(final MessageResolver messageResolver) throws IllegalArgumentException
249 	{
250 		Assert.argumentNotNull("messageResolver", messageResolver);
251 		Validator.messageResolver = messageResolver;
252 	}
253 
254 	/**
255 	 * @param formatter the messageValueFormatter to set
256 	 */
257 	public static void setMessageValueFormatter(final MessageValueFormatter formatter)
258 	{
259 		Assert.argumentNotNull("formatter", formatter);
260 		Validator.messageValueFormatter = formatter;
261 	}
262 
263 	private final Map<Class< ? >, ClassChecks> checksByClass = new WeakHashMap<Class< ? >, ClassChecks>();
264 
265 	private final Set<Configurer> configurers = new LinkedHashSet<Configurer>(4);
266 
267 	private final Map<String, ConstraintSet> constraintSetsById = collectionFactory.createMap(4);
268 
269 	protected final ThreadLocalLinkedList<Set<Object>> currentlyValidatedObjects = new ThreadLocalLinkedList<Set<Object>>();
270 
271 	protected final ThreadLocalLinkedList<List<ConstraintViolation>> currentViolations = new ThreadLocalLinkedList<List<ConstraintViolation>>();
272 
273 	private final Set<String> disabledProfiles = collectionFactory.createSet();
274 
275 	private final Set<String> enabledProfiles = collectionFactory.createSet();
276 
277 	private ExceptionTranslator exceptionTranslator;
278 
279 	protected final ExpressionLanguageRegistry expressionLanguageRegistry = new ExpressionLanguageRegistry();
280 
281 	private boolean isAllProfilesEnabledByDefault = true;
282 
283 	/**
284 	 * Flag that indicates any configuration method related to profiles was called.
285 	 * Used for performance improvements.
286 	 */
287 	private boolean isProfilesFeatureUsed = false;
288 
289 	protected final ObjectGraphNavigatorRegistry ognRegistry = new ObjectGraphNavigatorRegistry();
290 
291 	protected final DelegatingParameterNameResolver parameterNameResolver = new DelegatingParameterNameResolver(
292 			new ParameterNameResolverEnumerationImpl());
293 
294 	/**
295 	 * Constructs a new instance and uses a new instance of AnnotationsConfigurer
296 	 */
297 	public Validator()
298 	{
299 		ReflectionUtils.assertPrivateAccessAllowed();
300 		configurers.add(new AnnotationsConfigurer());
301 	}
302 
303 	/**
304 	 * Constructs a new instance and configures it using the given configurers
305 	 */
306 	public Validator(final Collection<Configurer> configurers)
307 	{
308 		ReflectionUtils.assertPrivateAccessAllowed();
309 		if (configurers != null)
310 		{
311 			this.configurers.addAll(configurers);
312 		}
313 	}
314 
315 	/**
316 	 * Constructs a new instance and configures it using the given configurers
317 	 */
318 	public Validator(final Configurer... configurers)
319 	{
320 		ReflectionUtils.assertPrivateAccessAllowed();
321 		if (configurers != null)
322 		{
323 			for (final Configurer configurer : configurers)
324 			{
325 				this.configurers.add(configurer);
326 			}
327 		}
328 	}
329 
330 	private void _addChecks(final ClassChecks cc, final ClassConfiguration classCfg) throws InvalidConfigurationException,
331 			ReflectionException
332 	{
333 		if (TRUE.equals(classCfg.overwrite))
334 		{
335 			cc.clear();
336 		}
337 
338 		if (classCfg.checkInvariants != null)
339 		{
340 			cc.isCheckInvariants = classCfg.checkInvariants;
341 		}
342 
343 		// cache the result for better performance
344 		final boolean applyFieldConstraintsToConstructors = TRUE.equals(classCfg.applyFieldConstraintsToConstructors);
345 		final boolean applyFieldConstraintsToSetters = TRUE.equals(classCfg.applyFieldConstraintsToSetters);
346 		final boolean assertParametersNotNull = TRUE.equals(classCfg.assertParametersNotNull);
347 		final NotNullCheck sharedNotNullCheck = assertParametersNotNull ? new NotNullCheck() : null;
348 
349 		try
350 		{
351 			/* ******************************
352 			 * apply object level checks
353 			 * ******************************/
354 			if (classCfg.objectConfiguration != null)
355 			{
356 				final ObjectConfiguration objectCfg = classCfg.objectConfiguration;
357 
358 				if (TRUE.equals(objectCfg.overwrite))
359 				{
360 					cc.clearObjectChecks();
361 				}
362 				cc.addObjectChecks(objectCfg.checks);
363 			}
364 
365 			/* ******************************
366 			 * apply field checks
367 			 * ******************************/
368 			if (classCfg.fieldConfigurations != null)
369 			{
370 				for (final FieldConfiguration fieldCfg : classCfg.fieldConfigurations)
371 				{
372 					final Field field = classCfg.type.getDeclaredField(fieldCfg.name);
373 
374 					if (TRUE.equals(fieldCfg.overwrite))
375 					{
376 						cc.clearFieldChecks(field);
377 					}
378 
379 					if (fieldCfg.checks != null && fieldCfg.checks.size() > 0)
380 					{
381 						cc.addFieldChecks(field, fieldCfg.checks);
382 					}
383 				}
384 			}
385 
386 			/* ******************************
387 			 * apply constructor parameter checks
388 			 * ******************************/
389 			if (classCfg.constructorConfigurations != null)
390 			{
391 				for (final ConstructorConfiguration ctorCfg : classCfg.constructorConfigurations)
392 				{
393 					// ignore constructors without parameters
394 					if (ctorCfg.parameterConfigurations == null)
395 					{
396 						continue;
397 					}
398 
399 					final Class< ? >[] paramTypes = new Class[ctorCfg.parameterConfigurations.size()];
400 
401 					for (int i = 0, l = ctorCfg.parameterConfigurations.size(); i < l; i++)
402 					{
403 						paramTypes[i] = ctorCfg.parameterConfigurations.get(i).type;
404 					}
405 
406 					final Constructor< ? > ctor = classCfg.type.getDeclaredConstructor(paramTypes);
407 
408 					if (TRUE.equals(ctorCfg.overwrite))
409 					{
410 						cc.clearConstructorChecks(ctor);
411 					}
412 
413 					if (TRUE.equals(ctorCfg.postCheckInvariants))
414 					{
415 						cc.methodsWithCheckInvariantsPost.add(ctor);
416 					}
417 
418 					final String[] paramNames = parameterNameResolver.getParameterNames(ctor);
419 
420 					for (int i = 0, l = ctorCfg.parameterConfigurations.size(); i < l; i++)
421 					{
422 						final ParameterConfiguration paramCfg = ctorCfg.parameterConfigurations.get(i);
423 
424 						if (TRUE.equals(paramCfg.overwrite))
425 						{
426 							cc.clearConstructorParameterChecks(ctor, i);
427 						}
428 
429 						if (paramCfg.hasChecks())
430 						{
431 							cc.addConstructorParameterChecks(ctor, i, paramCfg.checks);
432 						}
433 
434 						if (paramCfg.hasCheckExclusions())
435 						{
436 							cc.addConstructorParameterCheckExclusions(ctor, i, paramCfg.checkExclusions);
437 						}
438 
439 						if (assertParametersNotNull)
440 						{
441 							cc.addConstructorParameterChecks(ctor, i, sharedNotNullCheck);
442 						}
443 
444 						/* *******************
445 						 * applying field constraints to the single parameter of setter methods
446 						 * *******************/
447 						if (applyFieldConstraintsToConstructors)
448 						{
449 							final Field field = ReflectionUtils.getField(cc.clazz, paramNames[i]);
450 
451 							// check if a corresponding field has been found
452 							if (field != null && paramTypes[i].isAssignableFrom(field.getType()))
453 							{
454 								final AssertFieldConstraintsCheck check = new AssertFieldConstraintsCheck();
455 								check.setFieldName(field.getName());
456 								cc.addConstructorParameterChecks(ctor, i, check);
457 							}
458 						}
459 					}
460 				}
461 			}
462 
463 			/* ******************************
464 			 * apply method parameter and return value checks and pre/post conditions
465 			 * ******************************/
466 			if (classCfg.methodConfigurations != null)
467 			{
468 				for (final MethodConfiguration methodCfg : classCfg.methodConfigurations)
469 				{
470 					/* ******************************
471 					 * determine the method
472 					 * ******************************/
473 					final Method method;
474 
475 					if (methodCfg.parameterConfigurations == null || methodCfg.parameterConfigurations.size() == 0)
476 					{
477 						method = classCfg.type.getDeclaredMethod(methodCfg.name);
478 					}
479 					else
480 					{
481 						final Class< ? >[] paramTypes = new Class[methodCfg.parameterConfigurations.size()];
482 
483 						for (int i = 0, l = methodCfg.parameterConfigurations.size(); i < l; i++)
484 						{
485 							paramTypes[i] = methodCfg.parameterConfigurations.get(i).type;
486 						}
487 
488 						method = classCfg.type.getDeclaredMethod(methodCfg.name, paramTypes);
489 					}
490 
491 					if (TRUE.equals(methodCfg.overwrite))
492 					{
493 						cc.clearMethodChecks(method);
494 					}
495 
496 					/* ******************************
497 					 * applying field constraints to the single parameter of setter methods
498 					 * ******************************/
499 					if (applyFieldConstraintsToSetters)
500 					{
501 						final Field field = ReflectionUtils.getFieldForSetter(method);
502 
503 						// check if a corresponding field has been found
504 						if (field != null)
505 						{
506 							final AssertFieldConstraintsCheck check = new AssertFieldConstraintsCheck();
507 							check.setFieldName(field.getName());
508 							cc.addMethodParameterChecks(method, 0, check);
509 						}
510 					}
511 
512 					/* ******************************
513 					 * configure parameter constraints
514 					 * ******************************/
515 					if (methodCfg.parameterConfigurations != null && methodCfg.parameterConfigurations.size() > 0)
516 					{
517 						for (int i = 0, l = methodCfg.parameterConfigurations.size(); i < l; i++)
518 						{
519 							final ParameterConfiguration paramCfg = methodCfg.parameterConfigurations.get(i);
520 
521 							if (TRUE.equals(paramCfg.overwrite))
522 							{
523 								cc.clearMethodParameterChecks(method, i);
524 							}
525 
526 							if (paramCfg.hasChecks())
527 							{
528 								cc.addMethodParameterChecks(method, i, paramCfg.checks);
529 							}
530 
531 							if (paramCfg.hasCheckExclusions())
532 							{
533 								cc.addMethodParameterCheckExclusions(method, i, paramCfg.checkExclusions);
534 							}
535 
536 							if (assertParametersNotNull)
537 							{
538 								cc.addMethodParameterChecks(method, i, sharedNotNullCheck);
539 							}
540 						}
541 					}
542 
543 					/* ******************************
544 					 * configure return value constraints
545 					 * ******************************/
546 					if (methodCfg.returnValueConfiguration != null)
547 					{
548 						if (TRUE.equals(methodCfg.returnValueConfiguration.overwrite))
549 						{
550 							cc.clearMethodReturnValueChecks(method);
551 						}
552 
553 						if (methodCfg.returnValueConfiguration.checks != null && methodCfg.returnValueConfiguration.checks.size() > 0)
554 						{
555 							cc.addMethodReturnValueChecks(method, methodCfg.isInvariant, methodCfg.returnValueConfiguration.checks);
556 						}
557 					}
558 
559 					if (TRUE.equals(methodCfg.preCheckInvariants))
560 					{
561 						cc.methodsWithCheckInvariantsPre.add(method);
562 					}
563 
564 					/*
565 					 * configure pre conditions
566 					 */
567 					if (methodCfg.preExecutionConfiguration != null)
568 					{
569 						if (TRUE.equals(methodCfg.preExecutionConfiguration.overwrite))
570 						{
571 							cc.clearMethodPreChecks(method);
572 						}
573 
574 						if (methodCfg.preExecutionConfiguration.checks != null && methodCfg.preExecutionConfiguration.checks.size() > 0)
575 						{
576 							cc.addMethodPreChecks(method, methodCfg.preExecutionConfiguration.checks);
577 						}
578 					}
579 
580 					if (TRUE.equals(methodCfg.postCheckInvariants))
581 					{
582 						cc.methodsWithCheckInvariantsPost.add(method);
583 					}
584 
585 					/*
586 					 * configure post conditions
587 					 */
588 					if (methodCfg.postExecutionConfiguration != null)
589 					{
590 						if (TRUE.equals(methodCfg.postExecutionConfiguration.overwrite))
591 						{
592 							cc.clearMethodPostChecks(method);
593 						}
594 
595 						if (methodCfg.postExecutionConfiguration.checks != null && methodCfg.postExecutionConfiguration.checks.size() > 0)
596 						{
597 							cc.addMethodPostChecks(method, methodCfg.postExecutionConfiguration.checks);
598 						}
599 					}
600 				}
601 			}
602 		}
603 		catch (final NoSuchMethodException ex)
604 		{
605 			throw new MethodNotFoundException(ex);
606 		}
607 		catch (final NoSuchFieldException ex)
608 		{
609 			throw new FieldNotFoundException(ex);
610 		}
611 	}
612 
613 	private void _checkConstraint(final List<ConstraintViolation> violations, final Check check, final Object validatedObject,
614 			final Object valueToValidate, final OValContext context, final String[] profiles)
615 	{
616 		/*
617 		 * special handling of the AssertValid constraint
618 		 */
619 		if (check instanceof AssertValidCheck)
620 		{
621 			checkConstraintAssertValid(violations, (AssertValidCheck) check, validatedObject, valueToValidate, context, profiles);
622 			return;
623 		}
624 
625 		/*
626 		 * special handling of the constraint lists
627 		 */
628 		if (check instanceof ConstraintsCheck)
629 		{
630 			for (final Check innerCheck : ((ConstraintsCheck) check).checks)
631 			{
632 				checkConstraint(violations, innerCheck, validatedObject, valueToValidate, context, profiles, false);
633 			}
634 			return;
635 		}
636 
637 		/*
638 		 * special handling of the FieldConstraints constraint
639 		 */
640 		if (check instanceof AssertConstraintSetCheck)
641 		{
642 			checkConstraintAssertConstraintSet(violations, (AssertConstraintSetCheck) check, validatedObject, valueToValidate, context,
643 					profiles);
644 			return;
645 		}
646 
647 		/*
648 		 * special handling of the FieldConstraints constraint
649 		 */
650 		if (check instanceof AssertFieldConstraintsCheck)
651 		{
652 			checkConstraintAssertFieldConstraints(violations, (AssertFieldConstraintsCheck) check, validatedObject, valueToValidate,
653 					context, profiles);
654 			return;
655 		}
656 
657 		/*
658 		 * standard constraints handling
659 		 */
660 		if (!check.isSatisfied(validatedObject, valueToValidate, context, this))
661 		{
662 			final String errorMessage = renderMessage(context, valueToValidate, check.getMessage(), check.getMessageVariables());
663 			violations.add(new ConstraintViolation(check, errorMessage, validatedObject, valueToValidate, context));
664 		}
665 	}
666 
667 	/**
668 	 * Validate validatedObject based on the constraints of the given class.
669 	 */
670 	private void _validateObjectInvariants(final Object validatedObject, final Class< ? > clazz,
671 			final List<ConstraintViolation> violations, final String[] profiles) throws ValidationFailedException
672 	{
673 		assert validatedObject != null;
674 		assert clazz != null;
675 		assert violations != null;
676 
677 		// abort if the root class has been reached
678 		if (clazz == Object.class) return;
679 
680 		try
681 		{
682 			final ClassChecks cc = getClassChecks(clazz);
683 
684 			// validate field constraints
685 			for (final Field field : cc.constrainedFields)
686 			{
687 				final Collection<Check> checks = cc.checksForFields.get(field);
688 
689 				if (checks != null && checks.size() > 0)
690 				{
691 					final FieldContext ctx = ContextCache.getFieldContext(field);
692 					final Object valueToValidate = resolveValue(ctx, validatedObject);
693 
694 					for (final Check check : checks)
695 					{
696 						checkConstraint(violations, check, validatedObject, valueToValidate, ctx, profiles, false);
697 					}
698 				}
699 			}
700 
701 			// validate constraints on getter methods
702 			for (final Method getter : cc.constrainedMethods)
703 			{
704 				final Collection<Check> checks = cc.checksForMethodReturnValues.get(getter);
705 
706 				if (checks != null && checks.size() > 0)
707 				{
708 					final MethodReturnValueContext ctx = ContextCache.getMethodReturnValueContext(getter);
709 					final Object valueToValidate = resolveValue(ctx, validatedObject);
710 
711 					for (final Check check : checks)
712 					{
713 						checkConstraint(violations, check, validatedObject, valueToValidate, ctx, profiles, false);
714 					}
715 				}
716 			}
717 
718 			// validate object constraints
719 			if (cc.checksForObject.size() > 0)
720 			{
721 				final OValContext ctx = ContextCache.getClassContext(clazz);
722 				for (final Check check : cc.checksForObject)
723 				{
724 					checkConstraint(violations, check, validatedObject, validatedObject, ctx, profiles, false);
725 				}
726 			}
727 
728 			// if the super class is annotated to be validatable also validate it against the object
729 			_validateObjectInvariants(validatedObject, clazz.getSuperclass(), violations, profiles);
730 		}
731 		catch (final OValException ex)
732 		{
733 			throw new ValidationFailedException("Object validation failed. Class: " + clazz + " Validated object: " + validatedObject, ex);
734 		}
735 	}
736 
737 	/**
738 	 * Validates the static field and static getter constrains of the given class.
739 	 * Constraints specified for super classes are not taken in account.
740 	 */
741 	private void _validateStaticInvariants(final Class< ? > validatedClass, final List<ConstraintViolation> violations,
742 			final String[] profiles) throws ValidationFailedException
743 	{
744 		assert validatedClass != null;
745 		assert violations != null;
746 
747 		final ClassChecks cc = getClassChecks(validatedClass);
748 
749 		// validate static field constraints
750 		for (final Field field : cc.constrainedStaticFields)
751 		{
752 			final Collection<Check> checks = cc.checksForFields.get(field);
753 
754 			if (checks != null && checks.size() > 0)
755 			{
756 				final FieldContext ctx = ContextCache.getFieldContext(field);
757 				final Object valueToValidate = resolveValue(ctx, null);
758 
759 				for (final Check check : checks)
760 				{
761 					checkConstraint(violations, check, validatedClass, valueToValidate, ctx, profiles, false);
762 				}
763 			}
764 		}
765 
766 		// validate constraints on getter methods
767 		for (final Method getter : cc.constrainedStaticMethods)
768 		{
769 			final Collection<Check> checks = cc.checksForMethodReturnValues.get(getter);
770 
771 			if (checks != null && checks.size() > 0)
772 			{
773 				final MethodReturnValueContext ctx = ContextCache.getMethodReturnValueContext(getter);
774 				final Object valueToValidate = resolveValue(ctx, null);
775 
776 				for (final Check check : checks)
777 				{
778 					checkConstraint(violations, check, validatedClass, valueToValidate, ctx, profiles, false);
779 				}
780 			}
781 		}
782 	}
783 
784 	/**
785 	 * Registers object-level constraint checks
786 	 *
787 	 * @param clazz the class to register the checks for
788 	 * @param checks the checks to add
789 	 * @throws IllegalArgumentException if <code>clazz == null</code> or <code>checks == null</code> or checks is empty
790 	 */
791 	public void addChecks(final Class< ? > clazz, final Check... checks) throws IllegalArgumentException
792 	{
793 		Assert.argumentNotNull("clazz", clazz);
794 		Assert.argumentNotEmpty("checks", checks);
795 
796 		getClassChecks(clazz).addObjectChecks(checks);
797 	}
798 
799 	/**
800 	 * Registers constraint checks for the given field
801 	 *
802 	 * @param field the field to declare the checks for
803 	 * @param checks the checks to add
804 	 * @throws IllegalArgumentException if <code>field == null</code> or <code>checks == null</code> or checks is empty
805 	 */
806 	public void addChecks(final Field field, final Check... checks) throws IllegalArgumentException
807 	{
808 		Assert.argumentNotNull("field", field);
809 		Assert.argumentNotEmpty("checks", checks);
810 
811 		getClassChecks(field.getDeclaringClass()).addFieldChecks(field, checks);
812 	}
813 
814 	/**
815 	 * Registers constraint checks for the given getter's return value
816 	 *
817 	 * @param invariantMethod a non-void, non-parameterized method (usually a JavaBean Getter style method)
818 	 * @param checks the checks to add
819 	 * @throws IllegalArgumentException if <code>getter == null</code> or <code>checks == null</code>
820 	 * @throws InvalidConfigurationException if getter is not a getter method
821 	 */
822 	public void addChecks(final Method invariantMethod, final Check... checks) throws IllegalArgumentException,
823 			InvalidConfigurationException
824 	{
825 		Assert.argumentNotNull("invariantMethod", invariantMethod);
826 		Assert.argumentNotEmpty("checks", checks);
827 
828 		getClassChecks(invariantMethod.getDeclaringClass()).addMethodReturnValueChecks(invariantMethod, TRUE, checks);
829 	}
830 
831 	/**
832 	 * Registers a new constraint set.
833 	 *
834 	 * @param constraintSet cannot be null
835 	 * @throws ConstraintSetAlreadyDefinedException if <code>overwrite == false</code> and
836 	 * 												a constraint set with the given id exists already
837 	 * @throws IllegalArgumentException if <code>constraintSet == null</code>
838 	 * 									or <code>constraintSet.id == null</code>
839 	 * 									or <code>constraintSet.id.length == 0</code>
840 	 * @throws IllegalArgumentException if <code>constraintSet.id == null</code>
841 	 */
842 	public void addConstraintSet(final ConstraintSet constraintSet, final boolean overwrite) throws ConstraintSetAlreadyDefinedException,
843 			IllegalArgumentException
844 	{
845 		Assert.argumentNotNull("constraintSet", constraintSet);
846 		Assert.argumentNotEmpty("constraintSet.id", constraintSet.getId());
847 
848 		synchronized (constraintSetsById)
849 		{
850 			if (!overwrite && constraintSetsById.containsKey(constraintSet.getId()))
851 				throw new ConstraintSetAlreadyDefinedException(constraintSet.getId());
852 
853 			constraintSetsById.put(constraintSet.getId(), constraintSet);
854 		}
855 	}
856 
857 	public void assertValid(final Object validatedObject) throws IllegalArgumentException, ValidationFailedException,
858 			ConstraintsViolatedException
859 	{
860 		final List<ConstraintViolation> violations = validate(validatedObject);
861 
862 		if (violations.size() > 0) throw translateException(new ConstraintsViolatedException(violations));
863 	}
864 
865 	public void assertValidFieldValue(final Object validatedObject, final Field validatedField, final Object fieldValueToValidate)
866 			throws IllegalArgumentException, ValidationFailedException, ConstraintsViolatedException
867 	{
868 		final List<ConstraintViolation> violations = validateFieldValue(validatedObject, validatedField, fieldValueToValidate);
869 
870 		if (violations.size() > 0) throw translateException(new ConstraintsViolatedException(violations));
871 	}
872 
873 	protected void checkConstraint(final List<ConstraintViolation> violations, final Check check, Object validatedObject,
874 			Object valueToValidate, OValContext context, final String[] profiles, final boolean isContainerValue) throws OValException
875 	{
876 		if (!isAnyProfileEnabled(check.getProfiles(), profiles)) return;
877 
878 		if (!check.isActive(validatedObject, valueToValidate, this)) return;
879 
880 		final ConstraintTarget[] targets = check.getAppliesTo();
881 
882 		// only process the target expression if we are not already on a value inside the container object (collection, array, map)
883 		if (!isContainerValue)
884 		{
885 			String target = check.getTarget();
886 			if (target != null)
887 			{
888 				target = target.trim();
889 				if (target.length() > 0)
890 				{
891 					if (valueToValidate == null) return;
892 					final String[] chunks = target.split(":", 2);
893 					final String ognId, path;
894 					if (chunks.length == 1)
895 					{
896 						ognId = "";
897 						path = chunks[0];
898 					}
899 					else
900 					{
901 						ognId = chunks[0];
902 						path = chunks[1];
903 					}
904 					final ObjectGraphNavigationResult result = ognRegistry.getObjectGraphNavigator(ognId) //
905 							.navigateTo(valueToValidate, path);
906 					if (result == null) return;
907 					validatedObject = result.targetParent;
908 					valueToValidate = result.target;
909 					if (result.targetAccessor instanceof Field)
910 					{
911 						context = ContextCache.getFieldContext((Field) result.targetAccessor);
912 					}
913 					else
914 					{
915 						context = ContextCache.getMethodReturnValueContext((Method) result.targetAccessor);
916 					}
917 				}
918 			}
919 		}
920 
921 		final Class< ? > compileTimeType = context.getCompileTimeType();
922 
923 		final boolean isCollection = valueToValidate != null ? //
924 				valueToValidate instanceof Collection< ? > : //
925 				Collection.class.isAssignableFrom(compileTimeType);
926 		final boolean isMap = !isCollection && //
927 				(valueToValidate != null ? //
928 						valueToValidate instanceof Map< ? , ? > : //
929 						Map.class.isAssignableFrom(compileTimeType));
930 		final boolean isArray = !isCollection && !isMap && //
931 				(valueToValidate != null ? //
932 						valueToValidate.getClass().isArray() : //
933 						compileTimeType.isArray());
934 		final boolean isContainer = isCollection || isMap || isArray;
935 
936 		if (isContainer && valueToValidate != null)
937 			if (isCollection)
938 			{
939 				if (ArrayUtils.containsSame(targets, ConstraintTarget.VALUES)
940 						&& (!isContainerValue || ArrayUtils.containsSame(targets, ConstraintTarget.RECURSIVE)))
941 				{
942 					for (final Object item : (Collection< ? >) valueToValidate)
943 					{
944 						checkConstraint(violations, check, validatedObject, item, context, profiles, true);
945 					}
946 				}
947 			}
948 			else if (isMap)
949 			{
950 				if (ArrayUtils.containsSame(targets, ConstraintTarget.KEYS)
951 						&& (!isContainerValue || ArrayUtils.containsSame(targets, ConstraintTarget.RECURSIVE)))
952 				{
953 					for (final Object item : ((Map< ? , ? >) valueToValidate).keySet())
954 					{
955 						checkConstraint(violations, check, validatedObject, item, context, profiles, true);
956 					}
957 				}
958 
959 				if (ArrayUtils.containsSame(targets, ConstraintTarget.VALUES)
960 						&& (!isContainerValue || ArrayUtils.containsSame(targets, ConstraintTarget.RECURSIVE)))
961 				{
962 					for (final Object item : ((Map< ? , ? >) valueToValidate).values())
963 					{
964 						checkConstraint(violations, check, validatedObject, item, context, profiles, true);
965 					}
966 				}
967 			}
968 			else
969 			// array
970 			{
971 				if (ArrayUtils.containsSame(targets, ConstraintTarget.VALUES)
972 						&& (!isContainerValue || ArrayUtils.containsSame(targets, ConstraintTarget.RECURSIVE)))
973 				{
974 					for (final Object item : ArrayUtils.asList(valueToValidate))
975 					{
976 						checkConstraint(violations, check, validatedObject, item, context, profiles, true);
977 					}
978 				}
979 			}
980 
981 		if (isContainerValue || !isContainer || isContainer && ArrayUtils.containsSame(targets, ConstraintTarget.CONTAINER))
982 		{
983 			_checkConstraint(violations, check, validatedObject, valueToValidate, context, profiles);
984 		}
985 	}
986 
987 	protected void checkConstraintAssertConstraintSet(final List<ConstraintViolation> violations, final AssertConstraintSetCheck check,
988 			final Object validatedObject, final Object valueToValidate, final OValContext context, final String[] profiles)
989 			throws OValException
990 	{
991 		final ConstraintSet cs = getConstraintSet(check.getId());
992 
993 		if (cs == null) throw new UndefinedConstraintSetException(check.getId());
994 
995 		final Collection<Check> referencedChecks = cs.getChecks();
996 
997 		if (referencedChecks != null && referencedChecks.size() > 0)
998 		{
999 			for (final Check referencedCheck : referencedChecks)
1000 			{
1001 				checkConstraint(violations, referencedCheck, validatedObject, valueToValidate, context, profiles, false);
1002 			}
1003 		}
1004 	}
1005 
1006 	protected void checkConstraintAssertFieldConstraints(final List<ConstraintViolation> violations,
1007 			final AssertFieldConstraintsCheck check, final Object validatedObject, final Object valueToValidate, final OValContext context,
1008 			final String[] profiles) throws OValException
1009 	{
1010 		final Class< ? > targetClass;
1011 
1012 		/*
1013 		 * set the targetClass based on the validation context
1014 		 */
1015 		if (check.getDeclaringClass() != null && check.getDeclaringClass() != Void.class)
1016 		{
1017 			targetClass = check.getDeclaringClass();
1018 		}
1019 		else if (context instanceof ConstructorParameterContext)
1020 		{
1021 			// the class declaring the field must either be the class declaring the constructor or one of its super
1022 			// classes
1023 			targetClass = ((ConstructorParameterContext) context).getConstructor().getDeclaringClass();
1024 		}
1025 		else if (context instanceof MethodParameterContext)
1026 		{
1027 			// the class declaring the field must either be the class declaring the method or one of its super classes
1028 			targetClass = ((MethodParameterContext) context).getMethod().getDeclaringClass();
1029 		}
1030 		else if (context instanceof MethodReturnValueContext)
1031 		{
1032 			// the class declaring the field must either be the class declaring the getter or one of its super classes
1033 			targetClass = ((MethodReturnValueContext) context).getMethod().getDeclaringClass();
1034 		}
1035 		else
1036 		{
1037 			// the lowest class that is expected to declare the field (or one of its super classes)
1038 			targetClass = validatedObject.getClass();
1039 		}
1040 
1041 		// the name of the field whose constraints shall be used
1042 		String fieldName = check.getFieldName();
1043 
1044 		/*
1045 		 * calculate the field name based on the validation context if the @AssertFieldConstraints constraint didn't specify the field name
1046 		 */
1047 		if (fieldName == null || fieldName.length() == 0) if (context instanceof ConstructorParameterContext)
1048 		{
1049 			fieldName = ((ConstructorParameterContext) context).getParameterName();
1050 		}
1051 		else if (context instanceof MethodParameterContext)
1052 		{
1053 			fieldName = ((MethodParameterContext) context).getParameterName();
1054 		}
1055 		else if (context instanceof MethodReturnValueContext)
1056 		{
1057 			fieldName = ReflectionUtils.guessFieldName(((MethodReturnValueContext) context).getMethod());
1058 		}
1059 
1060 		/*
1061 		 * find the field based on fieldName and targetClass
1062 		 */
1063 		final Field field = ReflectionUtils.getFieldRecursive(targetClass, fieldName);
1064 
1065 		if (field == null)
1066 			throw new FieldNotFoundException("Field <" + fieldName + "> not found in class <" + targetClass + "> or its super classes.");
1067 
1068 		final ClassChecks cc = getClassChecks(field.getDeclaringClass());
1069 		final Collection<Check> referencedChecks = cc.checksForFields.get(field);
1070 		if (referencedChecks != null && referencedChecks.size() > 0)
1071 		{
1072 			for (final Check referencedCheck : referencedChecks)
1073 			{
1074 				checkConstraint(violations, referencedCheck, validatedObject, valueToValidate, context, profiles, false);
1075 			}
1076 		}
1077 	}
1078 
1079 	protected void checkConstraintAssertValid(final List<ConstraintViolation> violations, final AssertValidCheck check,
1080 			final Object validatedObject, final Object valueToValidate, final OValContext context, final String[] profiles)
1081 			throws OValException
1082 	{
1083 		if (valueToValidate == null) return;
1084 
1085 		// ignore circular dependencies
1086 		if (isCurrentlyValidated(valueToValidate)) return;
1087 
1088 		final List<ConstraintViolation> additionalViolations = collectionFactory.createList();
1089 		validateInvariants(valueToValidate, additionalViolations, profiles);
1090 
1091 		if (additionalViolations.size() != 0)
1092 		{
1093 			final String errorMessage = renderMessage(context, valueToValidate, check.getMessage(), check.getMessageVariables());
1094 
1095 			violations.add(new ConstraintViolation(check, errorMessage, validatedObject, valueToValidate, context, additionalViolations));
1096 		}
1097 	}
1098 
1099 	/**
1100 	 * Disables all constraints profiles globally, i.e. no configured constraint will be validated.
1101 	 */
1102 	public synchronized void disableAllProfiles()
1103 	{
1104 		isProfilesFeatureUsed = true;
1105 		isAllProfilesEnabledByDefault = false;
1106 
1107 		enabledProfiles.clear();
1108 		disabledProfiles.clear();
1109 	}
1110 
1111 	/**
1112 	 * Disables a constraints profile globally.
1113 	 *
1114 	 * @param profile the id of the profile
1115 	 */
1116 	public void disableProfile(final String profile)
1117 	{
1118 		isProfilesFeatureUsed = true;
1119 
1120 		if (isAllProfilesEnabledByDefault)
1121 		{
1122 			disabledProfiles.add(profile);
1123 		}
1124 		else
1125 		{
1126 			enabledProfiles.remove(profile);
1127 		}
1128 	}
1129 
1130 	/**
1131 	 * Enables all constraints profiles globally, i.e. all configured constraint will be validated.
1132 	 */
1133 	public synchronized void enableAllProfiles()
1134 	{
1135 		isProfilesFeatureUsed = true;
1136 		isAllProfilesEnabledByDefault = true;
1137 
1138 		enabledProfiles.clear();
1139 		disabledProfiles.clear();
1140 	}
1141 
1142 	/**
1143 	 * Enables a constraints profile globally.
1144 	 *
1145 	 * @param profile the id of the profile
1146 	 */
1147 	public void enableProfile(final String profile)
1148 	{
1149 		isProfilesFeatureUsed = true;
1150 
1151 		if (isAllProfilesEnabledByDefault)
1152 		{
1153 			disabledProfiles.remove(profile);
1154 		}
1155 		else
1156 		{
1157 			enabledProfiles.add(profile);
1158 		}
1159 	}
1160 
1161 	/**
1162 	 * Gets the object-level constraint checks for the given class
1163 	 *
1164 	 * @param clazz the class to get the checks for
1165 	 * @throws IllegalArgumentException if <code>clazz == null</code>
1166 	 */
1167 	public Check[] getChecks(final Class< ? > clazz) throws IllegalArgumentException
1168 	{
1169 		Assert.argumentNotNull("clazz", clazz);
1170 
1171 		final ClassChecks cc = getClassChecks(clazz);
1172 
1173 		final Set<Check> checks = cc.checksForObject;
1174 		return checks == null ? null : checks.toArray(new Check[checks.size()]);
1175 	}
1176 
1177 	/**
1178 	 * Gets the constraint checks for the given field
1179 	 *
1180 	 * @param field the field to get the checks for
1181 	 * @throws IllegalArgumentException if <code>field == null</code>
1182 	 */
1183 	public Check[] getChecks(final Field field) throws IllegalArgumentException
1184 	{
1185 		Assert.argumentNotNull("field", field);
1186 
1187 		final ClassChecks cc = getClassChecks(field.getDeclaringClass());
1188 
1189 		final Set<Check> checks = cc.checksForFields.get(field);
1190 		return checks == null ? null : checks.toArray(new Check[checks.size()]);
1191 	}
1192 
1193 	/**
1194 	 * Gets the constraint checks for the given method's return value
1195 	 *
1196 	 * @param method the method to get the checks for
1197 	 * @throws IllegalArgumentException if <code>getter == null</code>
1198 	 */
1199 	public Check[] getChecks(final Method method) throws IllegalArgumentException
1200 	{
1201 		Assert.argumentNotNull("method", method);
1202 
1203 		final ClassChecks cc = getClassChecks(method.getDeclaringClass());
1204 
1205 		final Set<Check> checks = cc.checksForMethodReturnValues.get(method);
1206 		return checks == null ? null : checks.toArray(new Check[checks.size()]);
1207 	}
1208 
1209 	/**
1210 	 * Returns the ClassChecks object for the particular class,
1211 	 * allowing you to modify the checks
1212 	 *
1213 	 * @param clazz cannot be null
1214 	 * @return returns the ClassChecks for the given class
1215 	 * @throws IllegalArgumentException if <code>clazz == null</code>
1216 	 */
1217 	protected ClassChecks getClassChecks(final Class< ? > clazz) throws IllegalArgumentException, InvalidConfigurationException,
1218 			ReflectionException
1219 	{
1220 		Assert.argumentNotNull("clazz", clazz);
1221 
1222 		synchronized (checksByClass)
1223 		{
1224 			ClassChecks cc = checksByClass.get(clazz);
1225 
1226 			if (cc == null)
1227 			{
1228 				cc = new ClassChecks(clazz, parameterNameResolver);
1229 
1230 				for (final Configurer configurer : configurers)
1231 				{
1232 					final ClassConfiguration classConfig = configurer.getClassConfiguration(clazz);
1233 					if (classConfig != null)
1234 					{
1235 						_addChecks(cc, classConfig);
1236 					}
1237 				}
1238 
1239 				checksByClass.put(clazz, cc);
1240 			}
1241 
1242 			return cc;
1243 		}
1244 	}
1245 
1246 	/**
1247 	 * @return the internal linked set with the registered configurers
1248 	 */
1249 	public Set<Configurer> getConfigurers()
1250 	{
1251 		return configurers;
1252 	}
1253 
1254 	/**
1255 	 * Returns the given constraint set.
1256 	 *
1257 	 * @param constraintSetId the id of the constraint set to retrieve
1258 	 * @return the constraint set or null if not found
1259 	 * @throws InvalidConfigurationException
1260 	 * @throws IllegalArgumentException if <code>constraintSetId</code> is null
1261 	 */
1262 	public ConstraintSet getConstraintSet(final String constraintSetId) throws InvalidConfigurationException, IllegalArgumentException
1263 	{
1264 		Assert.argumentNotNull("constraintSetsById", constraintSetsById);
1265 		synchronized (constraintSetsById)
1266 		{
1267 			ConstraintSet cs = constraintSetsById.get(constraintSetId);
1268 
1269 			if (cs == null)
1270 			{
1271 				for (final Configurer configurer : configurers)
1272 				{
1273 					final ConstraintSetConfiguration csc = configurer.getConstraintSetConfiguration(constraintSetId);
1274 					if (csc != null)
1275 					{
1276 						cs = new ConstraintSet(csc.id);
1277 						cs.setChecks(csc.checks);
1278 
1279 						addConstraintSet(cs, csc.overwrite != null && csc.overwrite);
1280 					}
1281 				}
1282 			}
1283 			return cs;
1284 		}
1285 	}
1286 
1287 	/**
1288 	 * @return the exceptionProcessor
1289 	 */
1290 	public ExceptionTranslator getExceptionTranslator()
1291 	{
1292 		return exceptionTranslator;
1293 	}
1294 
1295 	/**
1296 	 * @return the expressionLanguageRegistry
1297 	 */
1298 	public ExpressionLanguageRegistry getExpressionLanguageRegistry()
1299 	{
1300 		return expressionLanguageRegistry;
1301 	}
1302 
1303 	/**
1304 	 * @return the objectGraphNavigatorRegistry
1305 	 */
1306 	public ObjectGraphNavigatorRegistry getObjectGraphNavigatorRegistry()
1307 	{
1308 		return ognRegistry;
1309 	}
1310 
1311 	/**
1312 	 * Determines if at least one of the given profiles is enabled
1313 	 *
1314 	 * @param profilesOfCheck
1315 	 * @param enabledProfiles optional array of profiles (can be null)
1316 	 * @return Returns true if at least one of the given profiles is enabled.
1317 	 */
1318 	protected boolean isAnyProfileEnabled(final String[] profilesOfCheck, final String[] enabledProfiles)
1319 	{
1320 		if (enabledProfiles == null)
1321 		{
1322 			// use the global profile configuration
1323 			if (profilesOfCheck == null || profilesOfCheck.length == 0) return isProfileEnabled("default");
1324 
1325 			for (final String profile : profilesOfCheck)
1326 				if (isProfileEnabled(profile)) return true;
1327 		}
1328 		else
1329 		{
1330 			// use the local profile configuration
1331 			if (profilesOfCheck == null || profilesOfCheck.length == 0) return ArrayUtils.containsEqual(enabledProfiles, "default");
1332 
1333 			for (final String profile : profilesOfCheck)
1334 				if (ArrayUtils.containsEqual(enabledProfiles, profile)) return true;
1335 		}
1336 		return false;
1337 	}
1338 
1339 	/**
1340 	 * Determines if the given object is currently validated in the current thread
1341 	 *
1342 	 * @return Returns true if the given object is currently validated in the current thread.
1343 	 */
1344 	protected boolean isCurrentlyValidated(final Object object)
1345 	{
1346 		Assert.argumentNotNull("object", object);
1347 		return currentlyValidatedObjects.get().getLast().contains(object);
1348 	}
1349 
1350 	/**
1351 	 * Determines if the given profile is enabled.
1352 	 *
1353 	 * @return Returns true if the given profile is enabled.
1354 	 */
1355 	public boolean isProfileEnabled(final String profileId)
1356 	{
1357 		Assert.argumentNotNull("profileId", profileId);
1358 		if (isProfilesFeatureUsed)
1359 		{
1360 			if (isAllProfilesEnabledByDefault) return !disabledProfiles.contains(profileId);
1361 
1362 			return enabledProfiles.contains(profileId);
1363 		}
1364 		return true;
1365 	}
1366 
1367 	/**
1368 	 * clears the checks and constraint sets => a reconfiguration using the
1369 	 * currently registered configurers will automatically happen
1370 	 */
1371 	public void reconfigureChecks()
1372 	{
1373 		synchronized (checksByClass)
1374 		{
1375 			checksByClass.clear();
1376 		}
1377 		synchronized (constraintSetsById)
1378 		{
1379 			constraintSetsById.clear();
1380 		}
1381 	}
1382 
1383 	/**
1384 	 * Removes object-level constraint checks
1385 	 *
1386 	 * @throws IllegalArgumentException if <code>clazz == null</code> or <code>checks == null</code> or checks is empty
1387 	 */
1388 	public void removeChecks(final Class< ? > clazz, final Check... checks) throws IllegalArgumentException
1389 	{
1390 		Assert.argumentNotNull("clazz", clazz);
1391 		Assert.argumentNotEmpty("checks", checks);
1392 
1393 		getClassChecks(clazz).removeObjectChecks(checks);
1394 	}
1395 
1396 	/**
1397 	 * Removes constraint checks for the given field
1398 	 *
1399 	 * @throws IllegalArgumentException if <code>field == null</code> or <code>checks == null</code> or checks is empty
1400 	 */
1401 	public void removeChecks(final Field field, final Check... checks) throws IllegalArgumentException
1402 	{
1403 		Assert.argumentNotNull("field", field);
1404 		Assert.argumentNotEmpty("checks", checks);
1405 
1406 		getClassChecks(field.getDeclaringClass()).removeFieldChecks(field, checks);
1407 	}
1408 
1409 	/**
1410 	 * Removes constraint checks for the given getter's return value
1411 	 *
1412 	 * @param getter a JavaBean Getter style method
1413 	 * @throws IllegalArgumentException if <code>getter == null</code> or <code>checks == null</code>
1414 	 */
1415 	public void removeChecks(final Method getter, final Check... checks) throws IllegalArgumentException
1416 	{
1417 		Assert.argumentNotNull("getter", getter);
1418 		Assert.argumentNotEmpty("checks", checks);
1419 
1420 		getClassChecks(getter.getDeclaringClass()).removeMethodReturnValueChecks(getter, checks);
1421 	}
1422 
1423 	/**
1424 	 * Removes the constraint set with the given id
1425 	 *
1426 	 * @param id the id of the constraint set to remove, cannot be null
1427 	 * @return the removed constraint set
1428 	 * @throws IllegalArgumentException if <code>id == null</code>
1429 	 */
1430 	public ConstraintSet removeConstraintSet(final String id) throws IllegalArgumentException
1431 	{
1432 		Assert.argumentNotNull("id", id);
1433 
1434 		synchronized (constraintSetsById)
1435 		{
1436 			return constraintSetsById.remove(id);
1437 		}
1438 	}
1439 
1440 	protected String renderMessage(final OValContext context, final Object value, final String messageKey,
1441 			final Map<String, ? > messageValues)
1442 	{
1443 		String message = MessageRenderer.renderMessage(messageKey, messageValues);
1444 
1445 		// if there are no place holders in the message simply return it
1446 		if (message.indexOf('{') == -1) return message;
1447 
1448 		message = StringUtils.replaceAll(message, "{context}", contextRenderer.render(context));
1449 		message = StringUtils.replaceAll(message, "{invalidValue}", messageValueFormatter.format(value));
1450 
1451 		return message;
1452 	}
1453 
1454 	/**
1455 	 * Reports an additional constraint violation for the current validation cycle.
1456 	 * This method is intended to be executed by check implementations only.
1457 	 *
1458 	 * @param constraintViolation the constraint violation
1459 	 */
1460 	public void reportConstraintViolation(final ConstraintViolation constraintViolation)
1461 	{
1462 		Assert.argumentNotNull("constraintViolation", constraintViolation);
1463 		if (currentViolations.get().size() == 0)
1464 			throw new IllegalStateException("No active validation cycle found for the current thread.");
1465 		currentViolations.get().getLast().add(constraintViolation);
1466 	}
1467 
1468 	/**
1469 	 * @param validatedObject may be null for static fields
1470 	 */
1471 	protected Object resolveValue(final FieldContext ctx, final Object validatedObject)
1472 	{
1473 		return ReflectionUtils.getFieldValue(ctx.getField(), validatedObject);
1474 	}
1475 
1476 	/**
1477 	 * @param validatedObject may be null for static methods
1478 	 */
1479 	protected Object resolveValue(final MethodReturnValueContext ctx, final Object validatedObject)
1480 	{
1481 		return ReflectionUtils.invokeMethod(ctx.getMethod(), validatedObject);
1482 	}
1483 
1484 	/**
1485 	 * @param exceptionTranslator the exceptionTranslator to set
1486 	 */
1487 	public void setExceptionTranslator(final ExceptionTranslator exceptionTranslator)
1488 	{
1489 		this.exceptionTranslator = exceptionTranslator;
1490 	}
1491 
1492 	protected RuntimeException translateException(final OValException ex)
1493 	{
1494 		if (exceptionTranslator != null)
1495 		{
1496 			final RuntimeException rex = exceptionTranslator.translateException(ex);
1497 			if (rex != null) return rex;
1498 		}
1499 		return ex;
1500 	}
1501 
1502 	public List<ConstraintViolation> validate(final Object validatedObject) throws IllegalArgumentException, ValidationFailedException
1503 	{
1504 		Assert.argumentNotNull("validatedObject", validatedObject);
1505 
1506 		// create required objects for this validation cycle
1507 		final List<ConstraintViolation> violations = collectionFactory.createList();
1508 		currentViolations.get().add(violations);
1509 		currentlyValidatedObjects.get().add(new IdentitySet<Object>(4));
1510 
1511 		try
1512 		{
1513 			validateInvariants(validatedObject, violations, (String[]) null);
1514 			return violations;
1515 		}
1516 		finally
1517 		{
1518 			// remove the validation cycle related objects
1519 			currentViolations.get().removeLast();
1520 			currentlyValidatedObjects.get().removeLast();
1521 		}
1522 	}
1523 
1524 	public List<ConstraintViolation> validate(final Object validatedObject, final String... profiles) throws IllegalArgumentException,
1525 			ValidationFailedException
1526 	{
1527 		Assert.argumentNotNull("validatedObject", validatedObject);
1528 
1529 		// create required objects for this validation cycle
1530 		final List<ConstraintViolation> violations = collectionFactory.createList();
1531 		currentViolations.get().add(violations);
1532 		currentlyValidatedObjects.get().add(new IdentitySet<Object>(4));
1533 
1534 		try
1535 		{
1536 			validateInvariants(validatedObject, violations, profiles);
1537 			return violations;
1538 		}
1539 		finally
1540 		{
1541 			// remove the validation cycle related objects
1542 			currentViolations.get().removeLast();
1543 			currentlyValidatedObjects.get().removeLast();
1544 		}
1545 	}
1546 
1547 	public List<ConstraintViolation> validateFieldValue(final Object validatedObject, final Field validatedField,
1548 			final Object fieldValueToValidate) throws IllegalArgumentException, ValidationFailedException
1549 	{
1550 		Assert.argumentNotNull("validatedObject", validatedObject);
1551 		Assert.argumentNotNull("validatedField", validatedField);
1552 
1553 		// create required objects for this validation cycle
1554 		final List<ConstraintViolation> violations = collectionFactory.createList();
1555 		currentViolations.get().add(violations);
1556 		currentlyValidatedObjects.get().add(new IdentitySet<Object>(4));
1557 
1558 		try
1559 		{
1560 			final ClassChecks cc = getClassChecks(validatedField.getDeclaringClass());
1561 			final Collection<Check> checks = cc.checksForFields.get(validatedField);
1562 
1563 			if (checks == null || checks.size() == 0) return violations;
1564 
1565 			final FieldContext context = ContextCache.getFieldContext(validatedField);
1566 
1567 			for (final Check check : checks)
1568 			{
1569 				checkConstraint(violations, check, validatedObject, fieldValueToValidate, context, null, false);
1570 			}
1571 			return violations;
1572 		}
1573 		catch (final OValException ex)
1574 		{
1575 			throw new ValidationFailedException("Field validation failed. Field: " + validatedField + " Validated object: "
1576 					+ validatedObject, ex);
1577 		}
1578 		finally
1579 		{
1580 			// remove the validation cycle related objects
1581 			currentViolations.get().removeLast();
1582 			currentlyValidatedObjects.get().removeLast();
1583 		}
1584 	}
1585 
1586 	/**
1587 	 * validates the field and getter constrains of the given object.
1588 	 * if the given object is a class the static fields and getters
1589 	 * are validated.
1590 	 *
1591 	 * @param validatedObject the object to validate, cannot be null
1592 	 * @throws IllegalArgumentException if <code>validatedObject == null</code>
1593 	 */
1594 	protected void validateInvariants(final Object validatedObject, final List<ConstraintViolation> violations, final String[] profiles)
1595 			throws IllegalArgumentException, ValidationFailedException
1596 	{
1597 		Assert.argumentNotNull("validatedObject", validatedObject);
1598 
1599 		currentlyValidatedObjects.get().getLast().add(validatedObject);
1600 		if (validatedObject instanceof Class< ? >)
1601 		{
1602 			_validateStaticInvariants((Class< ? >) validatedObject, violations, profiles);
1603 		}
1604 		else
1605 		{
1606 			_validateObjectInvariants(validatedObject, validatedObject.getClass(), violations, profiles);
1607 		}
1608 	}
1609 
1610 }