package de.ppi.fuwesta.spring.mvc.oval; import static net.sf.oval.Validator.getCollectionFactory; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.persistence.Column; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.sf.oval.Check; import net.sf.oval.collection.CollectionFactory; import net.sf.oval.configuration.CheckInitializationListener; import net.sf.oval.configuration.Configurer; import net.sf.oval.configuration.annotation.AnnotationCheck; import net.sf.oval.configuration.annotation.AnnotationsConfigurer; import net.sf.oval.configuration.pojo.elements.ClassConfiguration; import net.sf.oval.configuration.pojo.elements.ConstraintSetConfiguration; import net.sf.oval.configuration.pojo.elements.FieldConfiguration; import net.sf.oval.configuration.pojo.elements.ObjectConfiguration; import net.sf.oval.exception.ReflectionException; import net.sf.oval.guard.Guarded; import net.sf.oval.integration.spring.SpringCheckInitializationListener; import net.sf.oval.internal.util.Assert; import net.sf.oval.internal.util.ReflectionUtils; import de.ppi.fuwesta.oval.validation.DBConstraint; import de.ppi.fuwesta.oval.validation.Unique; import de.ppi.fuwesta.oval.validation.UniqueCheck; /** * Configurer that configures constraints based on annotations tagged with * {@link DBConstraint} or a Unique-Check, bases on {@link Column}. Based on * {@link AnnotationsConfigurer}. * * @author niels * * * @see AnnotationsConfigurer */ public class DbCheckConfigurer implements Configurer { /** * The Logger for the controller. */ private static final Logger LOG = LoggerFactory .getLogger(DbCheckConfigurer.class); /** * Set of {@link CheckInitializationListener} to give Spring or Guice a * chance to work. */ private final Set<CheckInitializationListener> listeners = new LinkedHashSet<CheckInitializationListener>(2); /** * Add an object to the set of {@link CheckInitializationListener} to give * Spring or Guice a chance to work. * * @param listener a listener. * @return true if this set did not already contain the specified element */ public boolean addCheckInitializationListener( final CheckInitializationListener listener) { Assert.argumentNotNull("listener", "[listener] must not be null"); return listeners.add(listener); } /** * Initialiaze all field-defined checks. * * @param classCfg the {@link ClassConfiguration}. */ protected void configureFieldChecks(final ClassConfiguration classCfg) { final CollectionFactory cf = getCollectionFactory(); List<Check> checks = cf.createList(2); for (final Field field : classCfg.type.getDeclaredFields()) { // loop over all annotations of the current field for (final Annotation annotation : field.getAnnotations()) { // check if the current annotation is a constraint annotation if (annotation.annotationType().isAnnotationPresent( DBConstraint.class)) { checks.add(initializeCheck(annotation)); } else if (annotation instanceof Column) { if (((Column) annotation).unique() && !field.isAnnotationPresent(Unique.class)) { Check uniqueCheck = new UniqueCheck(); SpringCheckInitializationListener.INSTANCE .onCheckInitialized(uniqueCheck); checks.add(uniqueCheck); LOG.info("Adding a uniqueCheck to field >{}<!", field.toString()); } } } if (checks.size() > 0) { if (classCfg.fieldConfigurations == null) { classCfg.fieldConfigurations = cf.createSet(2); } final FieldConfiguration fc = new FieldConfiguration(); fc.name = field.getName(); fc.checks = checks; classCfg.fieldConfigurations.add(fc); checks = cf.createList(2); // create a new list for the next // field with checks } } } /** * Initialiaze all class-defined checks. * * @param classCfg the {@link ClassConfiguration}. */ protected void configureObjectLevelChecks(final ClassConfiguration classCfg) { final List<Check> checks = getCollectionFactory().createList(2); for (final Annotation annotation : ReflectionUtils.getAnnotations( classCfg.type, Boolean.TRUE.equals(classCfg.inspectInterfaces))) { // check if the current annotation is a constraint annotation if (annotation.annotationType().isAnnotationPresent( DBConstraint.class)) { checks.add(initializeCheck(annotation)); } } if (checks.size() > 0) { classCfg.objectConfiguration = new ObjectConfiguration(); classCfg.objectConfiguration.checks = checks; } } /** * {@inheritDoc} */ @Override public ClassConfiguration getClassConfiguration(final Class<?> clazz) { final ClassConfiguration classCfg = new ClassConfiguration(); classCfg.type = clazz; final Guarded guarded = clazz.getAnnotation(Guarded.class); if (guarded == null) { classCfg.applyFieldConstraintsToConstructors = Boolean.FALSE; classCfg.applyFieldConstraintsToSetters = Boolean.FALSE; classCfg.assertParametersNotNull = Boolean.FALSE; classCfg.checkInvariants = Boolean.FALSE; classCfg.inspectInterfaces = Boolean.FALSE; } else { classCfg.applyFieldConstraintsToConstructors = Boolean.valueOf(guarded .applyFieldConstraintsToConstructors()); classCfg.applyFieldConstraintsToSetters = Boolean.valueOf(guarded.applyFieldConstraintsToSetters()); classCfg.assertParametersNotNull = Boolean.valueOf(guarded.assertParametersNotNull()); classCfg.checkInvariants = Boolean.valueOf(guarded.checkInvariants()); classCfg.inspectInterfaces = Boolean.valueOf(guarded.inspectInterfaces()); } configureObjectLevelChecks(classCfg); configureFieldChecks(classCfg); return classCfg; } /** * {@inheritDoc} */ @Override public ConstraintSetConfiguration getConstraintSetConfiguration( final String constraintSetId) { return null; } /** * Initialize the check. * * @param constraintAnnotation the {@link DBConstraint}. * @param <ConstraintAnnotation> a subclass of annotation which defines an * constraint. * @return the {@link Check}. */ protected <ConstraintAnnotation extends Annotation> AnnotationCheck<ConstraintAnnotation> initializeCheck( final ConstraintAnnotation constraintAnnotation) { assert constraintAnnotation != null; final DBConstraint constraint = constraintAnnotation.annotationType().getAnnotation( DBConstraint.class); // determine the check class @SuppressWarnings("unchecked") final Class<AnnotationCheck<ConstraintAnnotation>> checkClass = (Class<AnnotationCheck<ConstraintAnnotation>>) constraint .checkWith(); // instantiate the appropriate check for the found constraint final AnnotationCheck<ConstraintAnnotation> check = newCheckInstance(checkClass); check.configure(constraintAnnotation); for (final CheckInitializationListener listener : listeners) { listener.onCheckInitialized(check); } return check; } /** * Creates an new {@link Check} instance. * * @param checkClass the class which should be instantiated. * @param <ConstraintAnnotation> a subclass of annotation which defines an * constraint. * * @return a new instance of the given constraint check implementation class */ protected <ConstraintAnnotation extends Annotation> AnnotationCheck<ConstraintAnnotation> newCheckInstance( final Class<AnnotationCheck<ConstraintAnnotation>> checkClass) { try { return checkClass.newInstance(); } catch (final InstantiationException ex) { throw new ReflectionException("Cannot initialize constraint check " + checkClass.getName(), ex); } catch (final IllegalAccessException ex) { throw new ReflectionException("Cannot initialize constraint check " + checkClass.getName(), ex); } } }