0001: /*******************************************************************************
0002: * Portions created by Sebastian Thomschke are copyright (c) 2005-2007 Sebastian
0003: * Thomschke.
0004: *
0005: * All Rights Reserved. This program and the accompanying materials
0006: * are made available under the terms of the Eclipse Public License v1.0
0007: * which accompanies this distribution, and is available at
0008: * http://www.eclipse.org/legal/epl-v10.html
0009: *
0010: * Contributors:
0011: * Sebastian Thomschke - initial implementation.
0012: *******************************************************************************/package net.sf.oval;
0013:
0014: import java.lang.reflect.Constructor;
0015: import java.lang.reflect.Field;
0016: import java.lang.reflect.Method;
0017: import java.util.Collection;
0018: import java.util.List;
0019: import java.util.Map;
0020: import java.util.Set;
0021: import java.util.WeakHashMap;
0022:
0023: import net.sf.oval.collection.CollectionFactory;
0024: import net.sf.oval.configuration.Configurer;
0025: import net.sf.oval.configuration.annotation.AnnotationsConfigurer;
0026: import net.sf.oval.configuration.pojo.elements.ClassConfiguration;
0027: import net.sf.oval.configuration.pojo.elements.ConstraintSetConfiguration;
0028: import net.sf.oval.configuration.pojo.elements.ConstructorConfiguration;
0029: import net.sf.oval.configuration.pojo.elements.FieldConfiguration;
0030: import net.sf.oval.configuration.pojo.elements.MethodConfiguration;
0031: import net.sf.oval.configuration.pojo.elements.ObjectConfiguration;
0032: import net.sf.oval.configuration.pojo.elements.ParameterConfiguration;
0033: import net.sf.oval.constraint.AssertConstraintSetCheck;
0034: import net.sf.oval.constraint.AssertFieldConstraintsCheck;
0035: import net.sf.oval.constraint.AssertValidCheck;
0036: import net.sf.oval.context.ClassContext;
0037: import net.sf.oval.context.ConstructorParameterContext;
0038: import net.sf.oval.context.FieldContext;
0039: import net.sf.oval.context.MethodParameterContext;
0040: import net.sf.oval.context.MethodReturnValueContext;
0041: import net.sf.oval.context.OValContext;
0042: import net.sf.oval.exception.ConstraintSetAlreadyDefinedException;
0043: import net.sf.oval.exception.ConstraintsViolatedException;
0044: import net.sf.oval.exception.ExceptionTranslator;
0045: import net.sf.oval.exception.ExpressionLanguageNotAvailableException;
0046: import net.sf.oval.exception.FieldNotFoundException;
0047: import net.sf.oval.exception.InvalidConfigurationException;
0048: import net.sf.oval.exception.MethodNotFoundException;
0049: import net.sf.oval.exception.OValException;
0050: import net.sf.oval.exception.UndefinedConstraintSetException;
0051: import net.sf.oval.exception.ValidationFailedException;
0052: import net.sf.oval.expression.ExpressionLanguage;
0053: import net.sf.oval.expression.ExpressionLanguageBeanShellImpl;
0054: import net.sf.oval.expression.ExpressionLanguageGroovyImpl;
0055: import net.sf.oval.expression.ExpressionLanguageJRubyImpl;
0056: import net.sf.oval.expression.ExpressionLanguageJavaScriptImpl;
0057: import net.sf.oval.expression.ExpressionLanguageMVELImpl;
0058: import net.sf.oval.expression.ExpressionLanguageOGNLImpl;
0059: import net.sf.oval.guard.ParameterNameResolver;
0060: import net.sf.oval.guard.ParameterNameResolverEnumerationImpl;
0061: import net.sf.oval.internal.ClassChecks;
0062: import net.sf.oval.internal.CollectionFactoryHolder;
0063: import net.sf.oval.internal.Log;
0064: import net.sf.oval.internal.MessageRenderer;
0065: import net.sf.oval.internal.MessageResolverHolder;
0066: import net.sf.oval.internal.util.ListOrderedSet;
0067: import net.sf.oval.internal.util.ReflectionUtils;
0068: import net.sf.oval.internal.util.StringUtils;
0069: import net.sf.oval.internal.util.ThreadLocalList;
0070: import net.sf.oval.localization.MessageResolver;
0071: import net.sf.oval.logging.LoggerFactory;
0072:
0073: /**
0074: * @author Sebastian Thomschke
0075: */
0076: public class Validator {
0077: private final static Log LOG = Log.getLog(Validator.class);
0078:
0079: /**
0080: * Returns a shared instance of the CollectionFactory
0081: */
0082: public static CollectionFactory getCollectionFactory() {
0083: return CollectionFactoryHolder.getFactory();
0084: }
0085:
0086: /**
0087: * @return the loggerFactory
0088: */
0089: public static LoggerFactory getLoggerFactory() {
0090: return Log.getLoggerFactory();
0091: }
0092:
0093: /**
0094: * @return the messageResolver
0095: */
0096: public static MessageResolver getMessageResolver() {
0097: return MessageResolverHolder.getMessageResolver();
0098: }
0099:
0100: /**
0101: *
0102: * @param factory the new collection factory to be used by all validator instances
0103: */
0104: public static void setCollectionFactory(
0105: final CollectionFactory factory)
0106: throws IllegalArgumentException {
0107: CollectionFactoryHolder.setFactory(factory);
0108: }
0109:
0110: /**
0111: * @param loggerFactory the loggerFactory to set
0112: */
0113: public static void setLoggerFactory(
0114: final LoggerFactory loggerFactory) {
0115: Log.setLoggerFactory(loggerFactory);
0116: }
0117:
0118: /**
0119: * @param messageResolver the messageResolver to set
0120: * @throws IllegalArgumentException if <code>messageResolver == null</code>
0121: */
0122: public static void setMessageResolver(
0123: final MessageResolver messageResolver)
0124: throws IllegalArgumentException {
0125: MessageResolverHolder.setMessageResolver(messageResolver);
0126: }
0127:
0128: private final Map<Class, ClassChecks> checksByClass = new WeakHashMap<Class, ClassChecks>();
0129:
0130: private final ListOrderedSet<Configurer> configurers = new ListOrderedSet<Configurer>(
0131: 4);
0132:
0133: private final Map<String, ConstraintSet> constraintSetsById = CollectionFactoryHolder
0134: .getFactory().createMap(4);
0135:
0136: private final ThreadLocalList<Object> currentlyValidatedObjects = new ThreadLocalList<Object>();
0137:
0138: protected Map<String, ExpressionLanguage> expressionLanguages = CollectionFactoryHolder
0139: .getFactory().createMap(4);
0140:
0141: /**
0142: * Flag that indicates any configuration method related to profiles was called.
0143: * Used for performance improvements.
0144: */
0145: private boolean isProfilesFeatureUsed = false;
0146:
0147: private boolean isAllProfilesEnabledByDefault = true;
0148:
0149: private final Set<String> disabledProfiles = CollectionFactoryHolder
0150: .getFactory().createSet();
0151:
0152: private final Set<String> enabledProfiles = CollectionFactoryHolder
0153: .getFactory().createSet();
0154:
0155: private ExceptionTranslator exceptionTranslator;
0156:
0157: protected ParameterNameResolver parameterNameResolver = new ParameterNameResolverEnumerationImpl();
0158:
0159: /**
0160: * Constructs a new validator object and uses a new isntance of
0161: * AnnotationsConfigurer
0162: */
0163: public Validator() {
0164: initializeDefaultELs();
0165:
0166: configurers.add(new AnnotationsConfigurer());
0167: }
0168:
0169: public Validator(final Collection<Configurer> configurers) {
0170: initializeDefaultELs();
0171:
0172: if (configurers != null) {
0173: this .configurers.addAll(configurers);
0174: }
0175: }
0176:
0177: public Validator(final Configurer... configurers) {
0178: initializeDefaultELs();
0179:
0180: if (configurers != null) {
0181: for (final Configurer configurer : configurers)
0182: this .configurers.add(configurer);
0183: }
0184: }
0185:
0186: /**
0187: * Registers object-level constraint checks
0188: *
0189: * @param clazz
0190: * @param checks
0191: * @throws IllegalArgumentException if <code>clazz == null</code> or <code>checks == null</code> or checks is empty
0192: */
0193: public void addChecks(final Class clazz, final Check... checks)
0194: throws IllegalArgumentException {
0195: if (clazz == null)
0196: throw new IllegalArgumentException("clazz cannot be null");
0197: if (checks == null)
0198: throw new IllegalArgumentException("checks cannot be null");
0199: if (checks.length == 0)
0200: throw new IllegalArgumentException("checks cannot empty");
0201:
0202: final ClassChecks cc = getClassChecks(clazz);
0203:
0204: cc.addObjectChecks(checks);
0205: }
0206:
0207: @SuppressWarnings("unchecked")
0208: protected void addChecks(final ClassChecks cc,
0209: final ClassConfiguration classConfig) throws OValException {
0210: if (Boolean.TRUE.equals(classConfig.overwrite)) {
0211: cc.clear();
0212: }
0213:
0214: if (classConfig.checkInvariants != null)
0215: cc.isCheckInvariants = classConfig.checkInvariants;
0216:
0217: try {
0218: /*
0219: * apply object level checks
0220: */
0221: if (classConfig.objectConfiguration != null) {
0222: final ObjectConfiguration objectConfig = classConfig.objectConfiguration;
0223: if (Boolean.TRUE.equals(objectConfig.overwrite)) {
0224: cc.clearObjectChecks();
0225: }
0226:
0227: cc.addObjectChecks(objectConfig.checks);
0228: }
0229:
0230: /*
0231: * apply field checks
0232: */
0233: if (classConfig.fieldConfigurations != null)
0234: for (final FieldConfiguration fieldConfig : classConfig.fieldConfigurations) {
0235: final Field field = classConfig.type
0236: .getDeclaredField(fieldConfig.name);
0237:
0238: if (Boolean.TRUE.equals(fieldConfig.overwrite)) {
0239: cc.clearFieldChecks(field);
0240: }
0241:
0242: if (fieldConfig.checks != null
0243: && fieldConfig.checks.size() > 0) {
0244: cc.addFieldChecks(field, fieldConfig.checks);
0245: }
0246: }
0247:
0248: /*
0249: * apply constructor parameter checks
0250: */
0251: if (classConfig.constructorConfigurations != null)
0252: for (final ConstructorConfiguration constructorConfig : classConfig.constructorConfigurations) {
0253: if (constructorConfig.parameterConfigurations != null) {
0254: final Class<?>[] parameterTypes = new Class[constructorConfig.parameterConfigurations
0255: .size()];
0256:
0257: for (int i = 0, l = constructorConfig.parameterConfigurations
0258: .size(); i < l; i++) {
0259: parameterTypes[i] = constructorConfig.parameterConfigurations
0260: .get(i).type;
0261: }
0262:
0263: final Constructor constructor = classConfig.type
0264: .getDeclaredConstructor(parameterTypes);
0265:
0266: final String[] parameterNames = parameterNameResolver
0267: .getParameterNames(constructor);
0268:
0269: if (Boolean.TRUE
0270: .equals(constructorConfig.overwrite)) {
0271: cc.clearConstructorChecks(constructor);
0272: }
0273:
0274: if (constructorConfig.postCheckInvariants != null
0275: && constructorConfig.postCheckInvariants)
0276: cc.methodsWithCheckInvariantsPost
0277: .add(constructor);
0278:
0279: for (int i = 0, l = constructorConfig.parameterConfigurations
0280: .size(); i < l; i++) {
0281: final ParameterConfiguration parameterConfig = constructorConfig.parameterConfigurations
0282: .get(i);
0283:
0284: if (Boolean.TRUE
0285: .equals(parameterConfig.overwrite)) {
0286: cc.clearConstructorParameterChecks(
0287: constructor, i);
0288: }
0289:
0290: final List<Check> checks = parameterConfig.checks;
0291:
0292: if (checks != null && checks.size() > 0) {
0293: cc.addConstructorParameterChecks(
0294: constructor, i, checks);
0295: }
0296:
0297: /* *******************
0298: * applying field constraints to the single parameter of setter methods
0299: * *******************/
0300: if (classConfig.applyFieldConstraintsToConstructors != null
0301: && classConfig.applyFieldConstraintsToConstructors
0302: .booleanValue()) {
0303: final Field field = ReflectionUtils
0304: .getField(cc.clazz,
0305: parameterNames[i]);
0306:
0307: // check if a corresponding field has been found
0308: if (field != null
0309: && parameterTypes[i]
0310: .isAssignableFrom(field
0311: .getType())) {
0312: final AssertFieldConstraintsCheck check = new AssertFieldConstraintsCheck();
0313: check.setFieldName(field.getName());
0314: cc.addConstructorParameterChecks(
0315: constructor, i, check);
0316: }
0317: }
0318: }
0319: }
0320: }
0321:
0322: /*
0323: * apply method parameter and return value checks and pre/post conditions
0324: */
0325: if (classConfig.methodConfigurations != null)
0326: for (final MethodConfiguration methodConfig : classConfig.methodConfigurations) {
0327: /*
0328: * determine the method
0329: */
0330: Method method = null;
0331:
0332: if (methodConfig.parameterConfigurations == null
0333: || methodConfig.parameterConfigurations
0334: .size() == 0) {
0335: method = classConfig.type
0336: .getDeclaredMethod(methodConfig.name);
0337: } else {
0338: final Class<?>[] parameterTypes = new Class[methodConfig.parameterConfigurations
0339: .size()];
0340:
0341: for (int i = 0, l = methodConfig.parameterConfigurations
0342: .size(); i < l; i++) {
0343: parameterTypes[i] = methodConfig.parameterConfigurations
0344: .get(i).type;
0345: }
0346:
0347: method = classConfig.type.getDeclaredMethod(
0348: methodConfig.name, parameterTypes);
0349: }
0350:
0351: if (Boolean.TRUE.equals(methodConfig.overwrite)) {
0352: cc.clearMethodChecks(method);
0353: }
0354:
0355: /* *******************
0356: * applying field constraints to the single parameter of setter methods
0357: * *******************/
0358: if (classConfig.applyFieldConstraintsToSetters != null
0359: && classConfig.applyFieldConstraintsToSetters
0360: .booleanValue()) {
0361: final Field field = ReflectionUtils
0362: .getFieldForSetter(method);
0363:
0364: // check if a corresponding field has been found
0365: if (field != null) {
0366: final AssertFieldConstraintsCheck check = new AssertFieldConstraintsCheck();
0367: check.setFieldName(field.getName());
0368: cc.addMethodParameterChecks(method, 0,
0369: check);
0370: }
0371: }
0372:
0373: /*
0374: * configure parameter constraints
0375: */
0376: if (methodConfig.parameterConfigurations != null
0377: && methodConfig.parameterConfigurations
0378: .size() > 0) {
0379: for (int i = 0, l = methodConfig.parameterConfigurations
0380: .size(); i < l; i++) {
0381: final ParameterConfiguration parameterConfig = methodConfig.parameterConfigurations
0382: .get(i);
0383:
0384: if (Boolean.TRUE
0385: .equals(parameterConfig.overwrite)) {
0386: cc
0387: .clearMethodParameterChecks(
0388: method, i);
0389: }
0390:
0391: final List<Check> checks = parameterConfig.checks;
0392:
0393: if (checks != null && checks.size() > 0) {
0394: cc.addMethodParameterChecks(method, i,
0395: checks);
0396: }
0397: }
0398: }
0399:
0400: /*
0401: * configure return value constraints
0402: */
0403: if (methodConfig.returnValueConfiguration != null) {
0404: if (Boolean.TRUE
0405: .equals(methodConfig.returnValueConfiguration.overwrite)) {
0406: cc.clearMethodReturnValueChecks(method);
0407: }
0408:
0409: if (methodConfig.returnValueConfiguration.checks != null
0410: && methodConfig.returnValueConfiguration.checks
0411: .size() > 0) {
0412: cc
0413: .addMethodReturnValueChecks(
0414: method,
0415: methodConfig.isInvariant,
0416: methodConfig.returnValueConfiguration.checks);
0417: }
0418: }
0419:
0420: if (methodConfig.preCheckInvariants != null
0421: && methodConfig.preCheckInvariants) {
0422: cc.methodsWithCheckInvariantsPre.add(method);
0423: }
0424:
0425: /*
0426: * configure pre conditions
0427: */
0428: if (methodConfig.preExecutionConfiguration != null) {
0429: if (Boolean.TRUE
0430: .equals(methodConfig.preExecutionConfiguration.overwrite)) {
0431: cc.clearMethodPreChecks(method);
0432: }
0433:
0434: if (methodConfig.preExecutionConfiguration.checks != null
0435: && methodConfig.preExecutionConfiguration.checks
0436: .size() > 0) {
0437: cc
0438: .addMethodPreChecks(
0439: method,
0440: methodConfig.preExecutionConfiguration.checks);
0441: }
0442: }
0443:
0444: if (methodConfig.postCheckInvariants != null
0445: && methodConfig.postCheckInvariants) {
0446: cc.methodsWithCheckInvariantsPost.add(method);
0447: }
0448:
0449: /*
0450: * configure post conditions
0451: */
0452: if (methodConfig.postExecutionConfiguration != null) {
0453: if (Boolean.TRUE
0454: .equals(methodConfig.postExecutionConfiguration.overwrite)) {
0455: cc.clearMethodPostChecks(method);
0456: }
0457:
0458: if (methodConfig.postExecutionConfiguration.checks != null
0459: && methodConfig.postExecutionConfiguration.checks
0460: .size() > 0) {
0461: cc
0462: .addMethodPostChecks(
0463: method,
0464: methodConfig.postExecutionConfiguration.checks);
0465: }
0466: }
0467: }
0468: } catch (final NoSuchMethodException ex) {
0469: throw new MethodNotFoundException(ex);
0470: } catch (final NoSuchFieldException ex) {
0471: throw new FieldNotFoundException(ex);
0472: }
0473: }
0474:
0475: /**
0476: * Registers constraint checks for the given field
0477: *
0478: * @param field
0479: * @param checks
0480: * @throws IllegalArgumentException if <code>field == null</code> or <code>checks == null</code> or checks is empty
0481: */
0482: public void addChecks(final Field field, final Check... checks)
0483: throws IllegalArgumentException {
0484: if (field == null)
0485: throw new IllegalArgumentException("field cannot be null");
0486: if (checks == null)
0487: throw new IllegalArgumentException("checks cannot be null");
0488: if (checks.length == 0)
0489: throw new IllegalArgumentException("checks cannot empty");
0490:
0491: final ClassChecks cc = getClassChecks(field.getDeclaringClass());
0492:
0493: cc.addFieldChecks(field, checks);
0494: }
0495:
0496: /**
0497: * Registers constraint checks for the given getter's return value
0498: *
0499: * @param invariantMethod a non-void, non-parameterized method (usually a JavaBean Getter style method)
0500: * @param checks
0501: * @throws IllegalArgumentException if <code>getter == null</code> or <code>checks == null</code>
0502: * @throws InvalidConfigurationException if getter is not a getter method
0503: */
0504: public void addChecks(final Method invariantMethod,
0505: final Check... checks) throws IllegalArgumentException,
0506: InvalidConfigurationException {
0507: if (invariantMethod == null)
0508: throw new IllegalArgumentException(
0509: "inVariantMethod cannot be null");
0510: if (checks == null)
0511: throw new IllegalArgumentException("checks cannot be null");
0512: if (checks.length == 0)
0513: throw new IllegalArgumentException("checks cannot empty");
0514:
0515: final ClassChecks cc = getClassChecks(invariantMethod
0516: .getDeclaringClass());
0517: cc.addMethodReturnValueChecks(invariantMethod, Boolean.TRUE,
0518: checks);
0519: }
0520:
0521: /**
0522: * Registers a new constraint set.
0523: *
0524: * @param constraintSet cannot be null
0525: * @param overwrite
0526: * @throws ConstraintSetAlreadyDefinedException if <code>overwrite == false</code> and a constraint set with the given id exists already
0527: * @throws IllegalArgumentException if <code>constraintSet == null</code> or <code>constraintSet.id == null</code> or <code>constraintSet.id.length == 0</code>
0528: * @throws IllegalArgumentException if <code>constraintSet.id == null</code>
0529: */
0530: public void addConstraintSet(final ConstraintSet constraintSet,
0531: boolean overwrite)
0532: throws ConstraintSetAlreadyDefinedException,
0533: IllegalArgumentException {
0534: if (constraintSet == null)
0535: throw new IllegalArgumentException(
0536: "constraintSet cannot be null");
0537:
0538: if (constraintSet.getId() == null)
0539: throw new IllegalArgumentException(
0540: "constraintSet.id cannot be null");
0541:
0542: if (constraintSet.getId().length() == 0)
0543: throw new IllegalArgumentException(
0544: "constraintSet.id cannot be empty");
0545:
0546: synchronized (constraintSetsById) {
0547: if (!overwrite
0548: && constraintSetsById.containsKey(constraintSet
0549: .getId()))
0550: throw new ConstraintSetAlreadyDefinedException(
0551: constraintSet.getId());
0552:
0553: constraintSetsById
0554: .put(constraintSet.getId(), constraintSet);
0555: }
0556: }
0557:
0558: /**
0559: *
0560: * @param languageId
0561: * @param expressionLanguage
0562: * @throws IllegalArgumentException if <code>languageId == null || expressionLanguage == null</code>
0563: */
0564: public void addExpressionLanguage(final String languageId,
0565: final ExpressionLanguage expressionLanguage)
0566: throws IllegalArgumentException {
0567: if (languageId == null)
0568: throw new IllegalArgumentException(
0569: "languageId cannot be null");
0570: if (expressionLanguage == null)
0571: throw new IllegalArgumentException(
0572: "expressionLanguage cannot be null");
0573:
0574: if (LOG.isInfo())
0575: LOG.info("Expression language '" + languageId
0576: + "' registered: " + expressionLanguage);
0577:
0578: expressionLanguages.put(languageId, expressionLanguage);
0579: }
0580:
0581: /**
0582: * validates the field and getter constrains of the given object
0583: * and throws an ConstraintsViolatedException if any constraint
0584: * violations are detected
0585: *
0586: * @param validatedObject the object to validate, cannot be null
0587: * @throws ConstraintsViolatedException
0588: * @throws ValidationFailedException
0589: * @throws IllegalArgumentException if <code>validatedObject == null</code>
0590: */
0591: public void assertValid(final Object validatedObject)
0592: throws IllegalArgumentException, ValidationFailedException,
0593: ConstraintsViolatedException {
0594: final List<ConstraintViolation> violations = validate(validatedObject);
0595:
0596: if (violations.size() > 0)
0597: throw translateException(new ConstraintsViolatedException(
0598: violations));
0599: }
0600:
0601: /**
0602: * Validates the give value against the defined field constraints and throws
0603: * an ConstraintsViolatedException if any constraint violations are detected.<br>
0604: *
0605: * @param validatedObject the object to validate, cannot be null
0606: * @param validatedField the field to validate, cannot be null
0607: * @throws IllegalArgumentException if <code>validatedObject == null</code> or <code>field == null</code>
0608: * @throws ConstraintsViolatedException
0609: * @throws ValidationFailedException
0610: */
0611: public void assertValidFieldValue(final Object validatedObject,
0612: final Field validatedField,
0613: final Object fieldValueToValidate)
0614: throws IllegalArgumentException, ValidationFailedException,
0615: ConstraintsViolatedException {
0616: final List<ConstraintViolation> violations = validateFieldValue(
0617: validatedObject, validatedField, fieldValueToValidate);
0618:
0619: if (violations.size() > 0)
0620: throw translateException(new ConstraintsViolatedException(
0621: violations));
0622: }
0623:
0624: protected void checkConstraint(
0625: final List<ConstraintViolation> violations,
0626: final Check check, final Object validatedObject,
0627: final Object valueToValidate, final OValContext context)
0628: throws OValException {
0629: if (!isAnyProfileEnabled(check.getProfiles()))
0630: return;
0631:
0632: /*
0633: * special handling of the AssertValid constraint
0634: */
0635: if (check instanceof AssertValidCheck) {
0636: checkConstraintAssertValid(violations,
0637: (AssertValidCheck) check, validatedObject,
0638: valueToValidate, context);
0639: return;
0640: }
0641:
0642: /*
0643: * special handling of the FieldConstraints constraint
0644: */
0645: if (check instanceof AssertConstraintSetCheck) {
0646: checkConstraintAssertConstraintSet(violations,
0647: (AssertConstraintSetCheck) check, validatedObject,
0648: valueToValidate, context);
0649: return;
0650: }
0651:
0652: /*
0653: * special handling of the FieldConstraints constraint
0654: */
0655: if (check instanceof AssertFieldConstraintsCheck) {
0656: checkConstraintAssertFieldConstraints(violations,
0657: (AssertFieldConstraintsCheck) check,
0658: validatedObject, valueToValidate, context);
0659: return;
0660: }
0661:
0662: /*
0663: * standard constraints handling
0664: */
0665: if (!check.isSatisfied(validatedObject, valueToValidate,
0666: context, this )) {
0667: final String errorMessage = renderMessage(context,
0668: valueToValidate, check.getMessage(), check
0669: .getMessageVariables());
0670: violations.add(new ConstraintViolation(
0671: check.getErrorCode(), errorMessage, check
0672: .getSeverity(), validatedObject,
0673: valueToValidate, context));
0674: }
0675: }
0676:
0677: protected void checkConstraintAssertConstraintSet(
0678: final List<ConstraintViolation> violations,
0679: final AssertConstraintSetCheck check,
0680: final Object validatedObject, final Object valueToValidate,
0681: final OValContext context) throws OValException {
0682: final ConstraintSet cs = getConstraintSet(check.getId());
0683:
0684: if (cs == null)
0685: throw new UndefinedConstraintSetException(check.getId());
0686:
0687: final Collection<Check> referencedChecks = cs.getChecks();
0688:
0689: if (referencedChecks != null && referencedChecks.size() > 0)
0690: for (final Check referencedCheck : referencedChecks) {
0691: checkConstraint(violations, referencedCheck,
0692: validatedObject, valueToValidate, context);
0693: }
0694: }
0695:
0696: protected void checkConstraintAssertFieldConstraints(
0697: final List<ConstraintViolation> violations,
0698: final AssertFieldConstraintsCheck check,
0699: final Object validatedObject, final Object valueToValidate,
0700: final OValContext context) throws OValException {
0701: Class targetClass;
0702:
0703: /*
0704: * set the targetClass based on the validation context
0705: */
0706: if (context instanceof ConstructorParameterContext) {
0707: // the class declaring the field must either be the class declaring the constructor or one of its super
0708: // classes
0709: targetClass = ((ConstructorParameterContext) context)
0710: .getConstructor().getDeclaringClass();
0711: } else if (context instanceof MethodParameterContext) {
0712: // the class declaring the field must either be the class declaring the method or one of its super classes
0713: targetClass = ((MethodParameterContext) context)
0714: .getMethod().getDeclaringClass();
0715: } else if (context instanceof MethodReturnValueContext) {
0716: // the class declaring the field must either be the class declaring the getter or one of its super classes
0717: targetClass = ((MethodReturnValueContext) context)
0718: .getMethod().getDeclaringClass();
0719: } else if (check.getDeclaringClass() != null
0720: && check.getDeclaringClass() != Void.class) {
0721: targetClass = check.getDeclaringClass();
0722: } else {
0723: // the lowest class that is expected to declare the field (or one of its super classes)
0724: targetClass = validatedObject.getClass();
0725:
0726: }
0727:
0728: // the name of the field whose constraints shall be used
0729: String fieldName = check.getFieldName();
0730:
0731: /*
0732: * calculate the field name based on the validation context if the @FieldConstraints constraint didn't specify the field name
0733: */
0734: if (fieldName == null || fieldName.length() == 0) {
0735: if (context instanceof ConstructorParameterContext) {
0736: fieldName = ((ConstructorParameterContext) context)
0737: .getParameterName();
0738: } else if (context instanceof MethodParameterContext) {
0739: fieldName = ((MethodParameterContext) context)
0740: .getParameterName();
0741: } else if (context instanceof MethodReturnValueContext) {
0742: /*
0743: * calculate the fieldName based on the getXXX isXXX style getter method name
0744: */
0745: fieldName = ReflectionUtils
0746: .guessFieldName(((MethodReturnValueContext) context)
0747: .getMethod());
0748: }
0749: }
0750:
0751: /*
0752: * find the field based on fieldName and targetClass
0753: */
0754: final Field field = ReflectionUtils.getFieldRecursive(
0755: targetClass, fieldName);
0756:
0757: if (field == null)
0758: throw new FieldNotFoundException("Field <" + fieldName
0759: + "> not found in class <" + targetClass
0760: + "> or its super classes.");
0761:
0762: final ClassChecks cc = getClassChecks(field.getDeclaringClass());
0763: final Collection<Check> referencedChecks = cc.checksForFields
0764: .get(field);
0765: if (referencedChecks != null && referencedChecks.size() > 0) {
0766: for (final Check referencedCheck : referencedChecks) {
0767: checkConstraint(violations, referencedCheck,
0768: validatedObject, valueToValidate, context);
0769: }
0770: }
0771: }
0772:
0773: protected void checkConstraintAssertValid(
0774: final List<ConstraintViolation> violations,
0775: final AssertValidCheck check, final Object validatedObject,
0776: final Object valueToValidate, final OValContext context)
0777: throws OValException {
0778: if (valueToValidate == null)
0779: return;
0780:
0781: // ignore circular dependencies
0782: if (isCurrentlyValidated(valueToValidate))
0783: return;
0784:
0785: final List<ConstraintViolation> additionalViolations = validate(valueToValidate);
0786:
0787: if (additionalViolations.size() != 0) {
0788: final String errorMessage = renderMessage(context,
0789: valueToValidate, check.getMessage(), check
0790: .getMessageVariables());
0791:
0792: violations.add(new ConstraintViolation(
0793: check.getErrorCode(), errorMessage, check
0794: .getSeverity(), validatedObject,
0795: valueToValidate, context, additionalViolations));
0796: }
0797:
0798: // if the value to validate is a collection also validate the collection items
0799: if (valueToValidate instanceof Collection
0800: && check.isRequireValidElements()) {
0801: for (final Object item : (Collection) valueToValidate) {
0802: checkConstraintAssertValid(violations, check,
0803: validatedObject, item, context);
0804: }
0805: }
0806:
0807: // if the value to validate is a map also validate the map keys and values
0808: else if (valueToValidate instanceof Map
0809: && check.isRequireValidElements()) {
0810: for (final Object item : ((Map) valueToValidate).keySet()) {
0811: checkConstraintAssertValid(violations, check,
0812: validatedObject, item, context);
0813: }
0814:
0815: for (final Object item : ((Map) valueToValidate).values()) {
0816: checkConstraintAssertValid(violations, check,
0817: validatedObject, item, context);
0818: }
0819: }
0820:
0821: // if the value to validate is an array also validate the array elements
0822: else if (valueToValidate.getClass().isArray()) {
0823: for (final Object item : (Object[]) valueToValidate) {
0824: checkConstraintAssertValid(violations, check,
0825: validatedObject, item, context);
0826: }
0827: }
0828: }
0829:
0830: /**
0831: * Enables all constraint profiles, i.e. all configured constraint will be validated.
0832: */
0833: public synchronized void disableAllProfiles() {
0834: isProfilesFeatureUsed = true;
0835: isAllProfilesEnabledByDefault = false;
0836:
0837: enabledProfiles.clear();
0838: disabledProfiles.clear();
0839: }
0840:
0841: /**
0842: * Disables a constraints profile.
0843: * @param profile the id of the profile
0844: */
0845: public void disableProfile(final String profile) {
0846: isProfilesFeatureUsed = true;
0847:
0848: if (isAllProfilesEnabledByDefault)
0849: disabledProfiles.add(profile);
0850: else
0851: enabledProfiles.remove(profile);
0852: }
0853:
0854: /**
0855: * Disables all constraint profiles, i.e. no configured constraint will be validated.
0856: */
0857: public synchronized void enableAllProfiles() {
0858: isProfilesFeatureUsed = true;
0859: isAllProfilesEnabledByDefault = true;
0860:
0861: enabledProfiles.clear();
0862: disabledProfiles.clear();
0863: }
0864:
0865: /**
0866: * Enables a constraints profile.
0867: * @param profile the id of the profile
0868: */
0869: public void enableProfile(final String profile) {
0870: isProfilesFeatureUsed = true;
0871:
0872: if (isAllProfilesEnabledByDefault)
0873: disabledProfiles.remove(profile);
0874: else
0875: enabledProfiles.add(profile);
0876: }
0877:
0878: /**
0879: * Gets the object-level constraint checks for the given class
0880: *
0881: * @param clazz
0882: * @throws IllegalArgumentException if <code>clazz == null</code>
0883: */
0884: public Check[] getChecks(final Class clazz)
0885: throws IllegalArgumentException {
0886: if (clazz == null)
0887: throw new IllegalArgumentException("clazz cannot be null");
0888:
0889: final ClassChecks cc = getClassChecks(clazz);
0890:
0891: final Set<Check> checks = cc.checksForObject;
0892: return checks == null ? null : checks.toArray(new Check[checks
0893: .size()]);
0894: }
0895:
0896: /**
0897: * Gets the constraint checks for the given field
0898: *
0899: * @param field
0900: * @throws IllegalArgumentException if <code>field == null</code>
0901: */
0902: public Check[] getChecks(final Field field)
0903: throws IllegalArgumentException {
0904: if (field == null)
0905: throw new IllegalArgumentException("field cannot be null");
0906:
0907: final ClassChecks cc = getClassChecks(field.getDeclaringClass());
0908:
0909: final Set<Check> checks = cc.checksForFields.get(field);
0910: return checks == null ? null : checks.toArray(new Check[checks
0911: .size()]);
0912: }
0913:
0914: /**
0915: * Gets the constraint checks for the given method's return value
0916: *
0917: * @param method
0918: * @throws IllegalArgumentException if <code>getter == null</code>
0919: */
0920: public Check[] getChecks(final Method method)
0921: throws IllegalArgumentException {
0922: if (method == null)
0923: throw new IllegalArgumentException("method cannot be null");
0924:
0925: final ClassChecks cc = getClassChecks(method
0926: .getDeclaringClass());
0927:
0928: final Set<Check> checks = cc.checksForMethodReturnValues
0929: .get(method);
0930: return checks == null ? null : checks.toArray(new Check[checks
0931: .size()]);
0932: }
0933:
0934: /**
0935: * Returns the ClassChecks object for the particular class,
0936: * allowing you to modify the checks
0937: *
0938: * @param clazz cannot be null
0939: * @return returns the ClassChecks for the given class
0940: * @throws IllegalArgumentException if <code>clazz == null</code>
0941: * @throws OValException
0942: */
0943: protected ClassChecks getClassChecks(final Class clazz)
0944: throws IllegalArgumentException, OValException {
0945: if (clazz == null)
0946: throw new IllegalArgumentException("clazz cannot be null");
0947:
0948: synchronized (checksByClass) {
0949: ClassChecks cc = checksByClass.get(clazz);
0950:
0951: if (cc == null) {
0952: cc = new ClassChecks(clazz);
0953:
0954: for (final Configurer configurer : configurers) {
0955: final ClassConfiguration classConfig = configurer
0956: .getClassConfiguration(clazz);
0957: if (classConfig != null)
0958: addChecks(cc, classConfig);
0959: }
0960:
0961: checksByClass.put(clazz, cc);
0962: }
0963:
0964: return cc;
0965: }
0966: }
0967:
0968: /**
0969: * @return the internal list with the registered configurers
0970: */
0971: public List<Configurer> getConfigurers() {
0972: return configurers;
0973: }
0974:
0975: public ConstraintSet getConstraintSet(final String constraintSetId)
0976: throws OValException {
0977: synchronized (constraintSetsById) {
0978: ConstraintSet cs = constraintSetsById.get(constraintSetId);
0979:
0980: if (cs == null) {
0981: for (final Configurer configurer : configurers) {
0982: final ConstraintSetConfiguration csc = configurer
0983: .getConstraintSetConfiguration(constraintSetId);
0984: if (csc != null) {
0985: cs = new ConstraintSet(csc.id);
0986: cs.setChecks(csc.checks);
0987:
0988: addConstraintSet(cs, csc.overwrite != null
0989: && csc.overwrite);
0990: }
0991: }
0992: }
0993: return cs;
0994: }
0995: }
0996:
0997: /**
0998: * @return the exceptionProcessor
0999: */
1000: public ExceptionTranslator getExceptionTranslator() {
1001: return exceptionTranslator;
1002: }
1003:
1004: /**
1005: *
1006: * @param languageId the id of the language, cannot be null
1007: *
1008: * @throws IllegalArgumentException if <code>languageName == null</code>
1009: * @throws ExpressionLanguageNotAvailableException
1010: */
1011: public ExpressionLanguage getExpressionLanguage(
1012: final String languageId) throws IllegalArgumentException,
1013: ExpressionLanguageNotAvailableException {
1014: if (languageId == null)
1015: throw new IllegalArgumentException(
1016: "languageId cannot be null");
1017:
1018: final ExpressionLanguage el = expressionLanguages
1019: .get(languageId);
1020:
1021: if (el == null)
1022: throw new ExpressionLanguageNotAvailableException(
1023: languageId);
1024:
1025: return el;
1026: }
1027:
1028: private void initializeDefaultELs() {
1029: // JavaScript support
1030: if (ReflectionUtils
1031: .isClassPresent("org.mozilla.javascript.Context")) {
1032: addExpressionLanguage("javascript",
1033: new ExpressionLanguageJavaScriptImpl());
1034: addExpressionLanguage("js",
1035: getExpressionLanguage("javascript"));
1036: }
1037:
1038: // Groovy support
1039: if (ReflectionUtils.isClassPresent("groovy.lang.Binding")) {
1040: addExpressionLanguage("groovy",
1041: new ExpressionLanguageGroovyImpl());
1042: }
1043:
1044: // BeanShell support
1045: if (ReflectionUtils.isClassPresent("bsh.Interpreter")) {
1046: addExpressionLanguage("bsh",
1047: new ExpressionLanguageBeanShellImpl());
1048: addExpressionLanguage("beanshell",
1049: getExpressionLanguage("bsh"));
1050: }
1051:
1052: // OGNL support
1053: if (ReflectionUtils.isClassPresent("ognl.Ognl")) {
1054: addExpressionLanguage("ognl",
1055: new ExpressionLanguageOGNLImpl());
1056: }
1057:
1058: // MVEL support
1059: if (ReflectionUtils.isClassPresent("org.mvel.MVEL")) {
1060: addExpressionLanguage("mvel",
1061: new ExpressionLanguageMVELImpl());
1062: }
1063:
1064: // JRuby support
1065: if (ReflectionUtils.isClassPresent("org.jruby.Ruby")) {
1066: addExpressionLanguage("ruby",
1067: new ExpressionLanguageJRubyImpl());
1068: addExpressionLanguage("jruby",
1069: getExpressionLanguage("ruby"));
1070: }
1071: }
1072:
1073: /**
1074: * Determines if at least one of the given profiles is enabled
1075: *
1076: * @param profileIds
1077: * @return Returns true if at least one of the given profiles is enabled.
1078: */
1079: protected boolean isAnyProfileEnabled(final String[] profileIds) {
1080: if (profileIds == null || profileIds.length == 0)
1081: return isAllProfilesEnabledByDefault;
1082:
1083: for (final String profile : profileIds) {
1084: if (isProfileEnabled(profile))
1085: return true;
1086: }
1087: return false;
1088: }
1089:
1090: /**
1091: * Determines if the given object is currently validated in the current thread
1092: *
1093: * @param object
1094: * @return Returns true if the given object is currently validated in the current thread.
1095: */
1096: protected boolean isCurrentlyValidated(final Object object) {
1097: return currentlyValidatedObjects.get().contains(object);
1098: }
1099:
1100: /**
1101: * Determines if the given profile is enabled.
1102: *
1103: * @param profileId
1104: * @return Returns true if the given profile is enabled.
1105: */
1106: public boolean isProfileEnabled(final String profileId) {
1107: if (isProfilesFeatureUsed) {
1108: if (isAllProfilesEnabledByDefault)
1109: return !disabledProfiles.contains(profileId);
1110:
1111: return enabledProfiles.contains(profileId);
1112: }
1113: return true;
1114: }
1115:
1116: /**
1117: * clears the checks and constraint sets => a reconfiguration using the
1118: * currently registered configurers will automatically happen
1119: */
1120: public void reconfigureChecks() {
1121: synchronized (checksByClass) {
1122: checksByClass.clear();
1123: }
1124: synchronized (constraintSetsById) {
1125: constraintSetsById.clear();
1126: }
1127: }
1128:
1129: /**
1130: * Removes object-level constraint checks
1131: *
1132: * @param clazz
1133: * @param checks
1134: * @throws IllegalArgumentException if <code>clazz == null</code> or <code>checks == null</code> or checks is empty
1135: */
1136: public void removeChecks(final Class clazz, final Check... checks)
1137: throws IllegalArgumentException {
1138: if (clazz == null)
1139: throw new IllegalArgumentException("clazz cannot be null");
1140: if (checks == null)
1141: throw new IllegalArgumentException("checks cannot be null");
1142: if (checks.length == 0)
1143: throw new IllegalArgumentException("checks cannot empty");
1144:
1145: final ClassChecks cc = getClassChecks(clazz);
1146: cc.removeObjectChecks(checks);
1147: }
1148:
1149: /**
1150: * Removes constraint checks for the given field
1151: *
1152: * @param field
1153: * @param checks
1154: * @throws IllegalArgumentException if <code>field == null</code> or <code>checks == null</code> or checks is empty
1155: */
1156: public void removeChecks(final Field field, final Check... checks)
1157: throws IllegalArgumentException {
1158: if (field == null)
1159: throw new IllegalArgumentException("field cannot be null");
1160: if (checks == null)
1161: throw new IllegalArgumentException("checks cannot be null");
1162: if (checks.length == 0)
1163: throw new IllegalArgumentException("checks cannot empty");
1164:
1165: final ClassChecks cc = getClassChecks(field.getDeclaringClass());
1166: cc.removeFieldChecks(field, checks);
1167: }
1168:
1169: /**
1170: * Removes constraint checks for the given getter's return value
1171: *
1172: * @param getter a JavaBean Getter style method
1173: * @param checks
1174: * @throws IllegalArgumentException if <code>getter == null</code> or <code>checks == null</code>
1175: */
1176: public void removeChecks(final Method getter, final Check... checks)
1177: throws IllegalArgumentException {
1178: if (getter == null)
1179: throw new IllegalArgumentException("field cannot be null");
1180: if (checks == null)
1181: throw new IllegalArgumentException("checks cannot be null");
1182: if (checks.length == 0)
1183: throw new IllegalArgumentException("checks cannot empty");
1184:
1185: final ClassChecks cc = getClassChecks(getter
1186: .getDeclaringClass());
1187: cc.removeMethodChecks(getter, checks);
1188: }
1189:
1190: /**
1191: * Removes the constraint set with the given id
1192: * @param id the id of the constraint set to remove, cannot be null
1193: * @return the removed constraint set
1194: * @throws IllegalArgumentException if <code>id == null</code>
1195: */
1196: public ConstraintSet removeConstraintSet(final String id)
1197: throws IllegalArgumentException {
1198: if (id == null)
1199: throw new IllegalArgumentException("id cannot be null");
1200: synchronized (constraintSetsById) {
1201: return constraintSetsById.remove(id);
1202: }
1203: }
1204:
1205: protected String renderMessage(final OValContext context,
1206: final Object value, final String messageKey,
1207: final Map<String, String> messageValues) {
1208: String message = MessageRenderer.renderMessage(messageKey,
1209: messageValues);
1210:
1211: // if there are no place holders in the message simply return it
1212: if (message.indexOf('{') == -1)
1213: return message;
1214:
1215: message = StringUtils.replaceAll(message, "{context}", context
1216: .toString());
1217: message = StringUtils.replaceAll(message, "{invalidValue}",
1218: value == null ? "null" : value.toString());
1219:
1220: return message;
1221: }
1222:
1223: /**
1224: * @param exceptionTranslator the exceptionTranslator to set
1225: */
1226: public void setExceptionTranslator(
1227: final ExceptionTranslator exceptionTranslator) {
1228: this .exceptionTranslator = exceptionTranslator;
1229: }
1230:
1231: protected RuntimeException translateException(final OValException ex) {
1232: if (exceptionTranslator != null) {
1233: final RuntimeException rex = exceptionTranslator
1234: .translateException(ex);
1235: if (rex != null)
1236: return rex;
1237: }
1238: return ex;
1239: }
1240:
1241: /**
1242: * validates the field and getter constrains of the given object
1243: *
1244: * @param validatedObject the object to validate, cannot be null
1245: * @return a list with the detected constraint violations. if no violations are detected an empty list is returned
1246: * @throws ValidationFailedException
1247: * @throws IllegalArgumentException if <code>validatedObject == null</code>
1248: */
1249: public List<ConstraintViolation> validate(
1250: final Object validatedObject)
1251: throws IllegalArgumentException, ValidationFailedException {
1252: if (validatedObject == null)
1253: throw new IllegalArgumentException(
1254: "validatedObject cannot be null");
1255:
1256: final List<ConstraintViolation> violations = CollectionFactoryHolder
1257: .getFactory().createList();
1258:
1259: validateInvariants(validatedObject, violations);
1260: return violations;
1261: }
1262:
1263: /**
1264: * Validates the give value against the defined field constraints.<br>
1265: *
1266: * @return a list with the detected constraint violations. if no violations are detected an empty list is returned
1267: * @throws IllegalArgumentException if <code>validatedObject == null</code> or <code>field == null</code>
1268: * @throws ValidationFailedException
1269: */
1270: public List<ConstraintViolation> validateFieldValue(
1271: final Object validatedObject, final Field validatedField,
1272: final Object fieldValueToValidate)
1273: throws IllegalArgumentException, ValidationFailedException {
1274: if (validatedObject == null)
1275: throw new IllegalArgumentException(
1276: "validatedObject cannot be null");
1277:
1278: if (validatedField == null)
1279: throw new IllegalArgumentException("field cannot be null");
1280:
1281: try {
1282: final ClassChecks cc = getClassChecks(validatedField
1283: .getDeclaringClass());
1284: final Collection<Check> checks = cc.checksForFields
1285: .get(validatedField);
1286:
1287: if (checks == null || checks.size() == 0)
1288: return null;
1289:
1290: final List<ConstraintViolation> violations = CollectionFactoryHolder
1291: .getFactory().createList();
1292:
1293: final FieldContext context = new FieldContext(
1294: validatedField);
1295:
1296: for (final Check check : checks) {
1297: checkConstraint(violations, check, validatedObject,
1298: fieldValueToValidate, context);
1299: }
1300: return violations;
1301: } catch (final OValException ex) {
1302: throw new ValidationFailedException(
1303: "Field validation failed. Field: " + validatedField
1304: + " Validated object: " + validatedObject,
1305: ex);
1306: }
1307: }
1308:
1309: /**
1310: * validates the field and getter constrains of the given object.
1311: * if the given object is a class the static fields and getters
1312: * are validated.
1313: *
1314: * @param validatedObject the object to validate, cannot be null
1315: * @throws ValidationFailedException
1316: * @throws IllegalArgumentException if <code>validatedObject == null</code>
1317: */
1318: protected void validateInvariants(final Object validatedObject,
1319: final List<ConstraintViolation> violations)
1320: throws IllegalArgumentException, ValidationFailedException {
1321: if (validatedObject == null)
1322: throw new IllegalArgumentException(
1323: "validatedObject cannot be null");
1324:
1325: currentlyValidatedObjects.get().add(validatedObject);
1326: try {
1327: if (validatedObject instanceof Class)
1328: validateStaticInvariants((Class) validatedObject,
1329: violations);
1330: else
1331: validateObjectInvariants(validatedObject,
1332: validatedObject.getClass(), violations);
1333: } finally {
1334: currentlyValidatedObjects.get().remove(validatedObject);
1335: }
1336: }
1337:
1338: /**
1339: * validate validatedObject based on the constraints of the given clazz
1340: */
1341: private void validateObjectInvariants(final Object validatedObject,
1342: final Class<?> clazz,
1343: final List<ConstraintViolation> violations)
1344: throws ValidationFailedException {
1345: assert validatedObject != null;
1346: assert clazz != null;
1347: assert violations != null;
1348:
1349: // abort if the root class has been reached
1350: if (clazz == Object.class)
1351: return;
1352:
1353: try {
1354: final ClassChecks cc = getClassChecks(clazz);
1355:
1356: // validate field constraints
1357: for (final Field field : cc.constrainedFields) {
1358: final Collection<Check> checks = cc.checksForFields
1359: .get(field);
1360:
1361: if (checks != null && checks.size() > 0) {
1362: final Object valueToValidate = ReflectionUtils
1363: .getFieldValue(field, validatedObject);
1364: final FieldContext ctx = new FieldContext(field);
1365:
1366: for (final Check check : checks) {
1367: checkConstraint(violations, check,
1368: validatedObject, valueToValidate, ctx);
1369: }
1370: }
1371: }
1372:
1373: // validate constraints on getter methods
1374: for (final Method getter : cc.constrainedMethods) {
1375: final Collection<Check> checks = cc.checksForMethodReturnValues
1376: .get(getter);
1377:
1378: if (checks != null && checks.size() > 0) {
1379: final Object valueToValidate = ReflectionUtils
1380: .invokeMethod(getter, validatedObject);
1381: final MethodReturnValueContext ctx = new MethodReturnValueContext(
1382: getter);
1383:
1384: for (final Check check : checks) {
1385: checkConstraint(violations, check,
1386: validatedObject, valueToValidate, ctx);
1387: }
1388: }
1389: }
1390:
1391: // validate object constraints
1392: if (cc.checksForObject.size() > 0) {
1393: final ClassContext ctx = new ClassContext(clazz);
1394: for (final Check check : cc.checksForObject) {
1395: checkConstraint(violations, check, validatedObject,
1396: validatedObject, ctx);
1397: }
1398: }
1399:
1400: // if the super class is annotated to be validatable also validate it against the object
1401: validateObjectInvariants(validatedObject, clazz
1402: .getSuperclass(), violations);
1403: } catch (final OValException ex) {
1404: throw new ValidationFailedException(
1405: "Object validation failed. Class: " + clazz
1406: + " Validated object: " + validatedObject,
1407: ex);
1408: }
1409: }
1410:
1411: /**
1412: * Validates the static field and static getter constrains of the given class.
1413: * Constraints specified for super classes are not taken in account.
1414: */
1415: private void validateStaticInvariants(final Class validatedClass,
1416: final List<ConstraintViolation> violations)
1417: throws ValidationFailedException {
1418: assert validatedClass != null;
1419: assert violations != null;
1420:
1421: final ClassChecks cc = getClassChecks(validatedClass);
1422:
1423: // validate static field constraints
1424: for (final Field field : cc.constrainedStaticFields) {
1425: final Collection<Check> checks = cc.checksForFields
1426: .get(field);
1427:
1428: if (checks != null && checks.size() > 0) {
1429: final Object valueToValidate = ReflectionUtils
1430: .getFieldValue(field, null);
1431: final FieldContext context = new FieldContext(field);
1432:
1433: for (final Check check : checks) {
1434: checkConstraint(violations, check, validatedClass,
1435: valueToValidate, context);
1436: }
1437: }
1438: }
1439:
1440: // validate constraints on getter methods
1441: for (final Method getter : cc.constrainedStaticMethods) {
1442: final Collection<Check> checks = cc.checksForMethodReturnValues
1443: .get(getter);
1444:
1445: if (checks != null && checks.size() > 0) {
1446: final Object valueToValidate = ReflectionUtils
1447: .invokeMethod(getter, null);
1448: final MethodReturnValueContext context = new MethodReturnValueContext(
1449: getter);
1450:
1451: for (final Check check : checks) {
1452: checkConstraint(violations, check, validatedClass,
1453: valueToValidate, context);
1454: }
1455: }
1456: }
1457: }
1458: }
|