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