Coverage Report - de.smartics.properties.spi.core.constraint.jsr303.GenericPropertyConstraint
 
Classes in this File Line Coverage Branch Coverage Complexity
GenericPropertyConstraint
0%
0/46
0%
0/10
2,625
 
 1  
 /*
 2  
  * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package de.smartics.properties.spi.core.constraint.jsr303;
 17  
 
 18  
 import java.io.IOException;
 19  
 import java.io.ObjectInputStream;
 20  
 import java.lang.annotation.Annotation;
 21  
 import java.util.List;
 22  
 import java.util.Locale;
 23  
 
 24  
 import javax.validation.ConstraintValidator;
 25  
 import javax.validation.ConstraintValidatorContext;
 26  
 import javax.validation.constraints.NotNull;
 27  
 
 28  
 import org.hibernate.validator.constraints.NotBlank;
 29  
 import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
 30  
 
 31  
 import de.smartics.properties.spi.core.constraint.AbstractPropertyConstraint;
 32  
 import de.smartics.util.lang.Arguments;
 33  
 import de.smartics.util.lang.NullArgumentException;
 34  
 
 35  
 /**
 36  
  * Generic wrapper for JSR-303 constraints.
 37  
  *
 38  
  * @param <A> the constraint annotation.
 39  
  * @param <T> the type of the property that is validated.
 40  
  */
 41  
 public class GenericPropertyConstraint<A extends Annotation, T> extends
 42  
     AbstractPropertyConstraint<T>
 43  
 {
 44  
   // ********************************* Fields *********************************
 45  
 
 46  
   // --- constants ------------------------------------------------------------
 47  
 
 48  
   /**
 49  
    * Appease PMD.
 50  
    */
 51  
   private static final String UNCHECKED = "unchecked";
 52  
 
 53  
   /**
 54  
    * The class version identifier.
 55  
    * <p>
 56  
    * The value of this constant is {@value}.
 57  
    */
 58  
   private static final long serialVersionUID = 1L;
 59  
 
 60  
   /**
 61  
    * Helper to fetch validators for default constraints.
 62  
    */
 63  0
   private static final ConstraintHelper CONSTRAINTS = new ConstraintHelper();
 64  
 
 65  
   // --- members --------------------------------------------------------------
 66  
 
 67  
   /**
 68  
    * The validator to validate values.
 69  
    */
 70  
   private transient ConstraintValidator<A, T> validator;
 71  
 
 72  
   // FIXME: Support list of validators.
 73  
 
 74  
   /**
 75  
    * The JSR-303 constraint annotation.
 76  
    *
 77  
    * @serial
 78  
    */
 79  
   private final A constraintAnnotation;
 80  
 
 81  
   /**
 82  
    * Stores the validator class for serialization.
 83  
    *
 84  
    * @serial
 85  
    */
 86  
   private final Class<? extends ConstraintValidator<A, T>> validatorClass;
 87  
 
 88  
   // ****************************** Initializer *******************************
 89  
 
 90  
   // ****************************** Constructors ******************************
 91  
 
 92  
   /**
 93  
    * Convenience constructor if the validator can be derived from the
 94  
    * {@code constraintAnnotation}.
 95  
    *
 96  
    * @param constraintAnnotation the JSR-303 constraint annotation.
 97  
    * @throws NullArgumentException if {@code constraintAnnotation} is
 98  
    *           <code>null</code>.
 99  
    */
 100  
   @SuppressWarnings(UNCHECKED)
 101  
   public GenericPropertyConstraint(final A constraintAnnotation)
 102  
     throws NullArgumentException
 103  
   {
 104  0
     this((ConstraintValidator<A, T>) createValidator(constraintAnnotation),
 105  
         constraintAnnotation);
 106  0
   }
 107  
 
 108  
   // /**
 109  
   // * Convenience constructor if to provide the validator as a class.
 110  
   // *
 111  
   // * @param validatorClass the class of the validator to validate values.
 112  
   // * @param constraintAnnotation the JSR-303 constraint annotation.
 113  
   // * @throws NullArgumentException if {@code constraintAnnotation} is
 114  
   // * <code>null</code>.
 115  
   // */
 116  
   // public GenericPropertyConstraint(
 117  
   // final Class<ConstraintValidator<A, T>> validatorClass,
 118  
   // final A constraintAnnotation) throws NullArgumentException
 119  
   // {
 120  
   // this((ConstraintValidator<A, T>) createValidator(validatorClass),
 121  
   // constraintAnnotation);
 122  
   // }
 123  
 
 124  
   /**
 125  
    * Default constructor.
 126  
    *
 127  
    * @param validator the validator to validate values.
 128  
    * @param constraintAnnotation the JSR-303 constraint annotation.
 129  
    * @throws NullArgumentException if either {@code validator} or
 130  
    *           {@code constraintAnnotation} is <code>null</code>.
 131  
    */
 132  
   @SuppressWarnings(UNCHECKED)
 133  
   public GenericPropertyConstraint(final ConstraintValidator<A, T> validator,
 134  
       final A constraintAnnotation) throws NullArgumentException
 135  0
   {
 136  0
     Arguments.checkNotNull("validator", validator);
 137  0
     Arguments.checkNotNull("constraintAnnotation", constraintAnnotation);
 138  
 
 139  0
     this.validator = validator;
 140  0
     this.constraintAnnotation = constraintAnnotation;
 141  0
     this.validatorClass =
 142  
         (Class<? extends ConstraintValidator<A, T>>) validator.getClass();
 143  
 
 144  0
     validator.initialize(constraintAnnotation);
 145  0
   }
 146  
 
 147  
   // ****************************** Inner Classes *****************************
 148  
 
 149  
   // ********************************* Methods ********************************
 150  
 
 151  
   // --- init -----------------------------------------------------------------
 152  
 
 153  
   @SuppressWarnings({ UNCHECKED, "rawtypes" })
 154  
   private static <A extends Annotation, T> ConstraintValidator<A, T> createValidator(
 155  
       final A constraintAnnotation) throws IllegalArgumentException
 156  
   {
 157  0
     final javax.validation.Constraint annotation =
 158  
         constraintAnnotation.annotationType().getAnnotation(
 159  
             javax.validation.Constraint.class);
 160  0
     if (annotation != null)
 161  
     {
 162  0
       final Class<? extends ConstraintValidator<?, ?>>[] validatorClass =
 163  
           annotation.validatedBy();
 164  0
       if (validatorClass.length > 0)
 165  
       {
 166  0
         final Class<? extends ConstraintValidator<A, T>> type =
 167  
             (Class<ConstraintValidator<A, T>>) validatorClass[0];
 168  0
         final ConstraintValidator validator =
 169  
             (ConstraintValidator) createValidator(type);
 170  0
         return validator;
 171  
       }
 172  
       else
 173  
       {
 174  0
         final Class<? extends Annotation> annotationType =
 175  
             constraintAnnotation.annotationType();
 176  0
         final List<Class<? extends ConstraintValidator<? extends Annotation, ?>>> validators =
 177  
             CONSTRAINTS.getBuiltInConstraints(annotationType);
 178  0
         if (!validators.isEmpty())
 179  
         {
 180  0
           final Class<? extends ConstraintValidator<A, T>> type =
 181  
               (Class<? extends ConstraintValidator<A, T>>) validators.get(0);
 182  0
           final ConstraintValidator validator =
 183  
               (ConstraintValidator) createValidator(type);
 184  0
           return validator;
 185  
         }
 186  
       }
 187  
     }
 188  
 
 189  0
     throw new IllegalArgumentException(
 190  
         "Cannot derive constraint validator from annotation: "
 191  
             + constraintAnnotation);
 192  
   }
 193  
 
 194  
   private static <A extends Annotation, T> ConstraintValidator<A, T> createValidator(
 195  
       final Class<? extends ConstraintValidator<A, T>> validatorClass)
 196  
     throws IllegalArgumentException
 197  
   {
 198  0
     Throwable cause = null;
 199  
     try
 200  
     {
 201  0
       final ConstraintValidator<A, T> validator =
 202  
           (ConstraintValidator<A, T>) validatorClass.newInstance();
 203  0
       return validator;
 204  
     }
 205  0
     catch (final InstantiationException e)
 206  
     {
 207  0
       cause = e;
 208  
       // Fail at the end
 209  
     }
 210  0
     catch (final IllegalAccessException e)
 211  
     {
 212  0
       cause = e;
 213  
       // Fail at the end
 214  0
     }
 215  
 
 216  0
     throw new IllegalArgumentException(
 217  
         "Cannot instantiate constraint validator: " + validatorClass.getName(),
 218  
         cause);
 219  
   }
 220  
 
 221  
   // --- get&set --------------------------------------------------------------
 222  
 
 223  
   // --- business -------------------------------------------------------------
 224  
 
 225  
   /**
 226  
    * {@inheritDoc}
 227  
    * <p>
 228  
    * Override to provide more specific information the the string representation
 229  
    * of the constraint annotation.
 230  
    * </p>
 231  
    *
 232  
    * @see de.smartics.properties.api.core.domain.PropertyConstraint#getDescription(java.util.Locale)
 233  
    */
 234  
   // CHECKSTYLE:OFF
 235  
   @Override
 236  
   public String getDescription(final Locale locale) // CHECKSTYLE:ON
 237  
   {
 238  0
     return new ConstraintPrettifier(constraintAnnotation).toString();
 239  
   }
 240  
 
 241  
   @Override
 242  
   public final boolean isValid(final T value)
 243  
   {
 244  
     // Unsure if the context may be null, therefore we fake it:
 245  0
     final ConstraintValidatorContext context =
 246  
         new PropertyConstraintValidatorContext();
 247  
     // new ConstraintValidatorContextImpl(PathImpl.createRootPath(),
 248  
     // new ConstraintDescriptorImpl<A>(constraintAnnotation,
 249  
     // new ConstraintHelper(), ElementType.FIELD,
 250  
     // ConstraintOrigin.DEFINED_LOCALLY));
 251  0
     final boolean valid = validator.isValid(value, context);
 252  0
     return valid;
 253  
   }
 254  
 
 255  
   @Override
 256  
   public final boolean isMandatoryConstraint()
 257  
   {
 258  0
     final Class<? extends Annotation> type =
 259  
         constraintAnnotation.annotationType();
 260  0
     return NotNull.class == type || NotBlank.class == type;
 261  
   }
 262  
 
 263  
   // --- object basics --------------------------------------------------------
 264  
 
 265  
   /**
 266  
    * Reads the object from the given stream.
 267  
    *
 268  
    * @param in the stream to read from.
 269  
    * @throws IOException on read problems.
 270  
    * @throws ClassNotFoundException if a class cannot be found.
 271  
    */
 272  
   private void readObject(final ObjectInputStream in) throws IOException,
 273  
     ClassNotFoundException
 274  
   {
 275  0
     in.defaultReadObject();
 276  
 
 277  
     try
 278  
     {
 279  0
       this.validator = validatorClass.newInstance();
 280  
     }
 281  0
     catch (final Exception e)
 282  
     {
 283  0
       throw new IOException("Cannot deserialize validator.", e);
 284  0
     }
 285  0
   }
 286  
 }