Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /* 
   * Copyright (C) 2012 FoxLabs
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   * http://www.apache.org/licenses/LICENSE-2.0
   * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package org.foxlabs.validation.support;
 
 
 
 import java.util.Set;
 
 
This class provides annotation support methods and maintains validation components cache.

The support methods allow to extract annotation properties used for building validation components.

The cache used to store Validation instances having corresponding annotations. If Validation implementation class has default constructor or constructor that accepts type of the value to be validated then instances of this class can be cached. In other cases Validation instance can't be cached because its constructor takes arguments that can differ in each instance for the same annotation type and value type. The cache stores such validation components as key-value pairs where value is validation component instance and key is combination of annotation type and type of the value to be validated.

AnnotationSupport is thread-safe.

Author(s):
Fox Mulder
See also:
org.foxlabs.validation.Validation
 
 public abstract class AnnotationSupport {
     
     // Annotation cache
     
    
The cache used to store Validation instances.
 
     private static final ConcurrentMap<CacheKeyValidation<?>> cache =
             new ConcurrentHashMap<CacheKeyValidation<?>>();
    
    
Cache key of Validation instances.

Author(s):
Fox Mulder
 
     private static final class CacheKey {
        
        
Annotation type.
 
         private final Class<? extends AnnotationannotationType;
        
        
The type of a value to be validated.
 
         private final Class<?> valueType;
        
        
Constructs a new CacheKey with the specified annotation and value types.

Parameters:
annotationType Annotation type.
valueType The type of a value to be validated.
 
         private CacheKey(Class<? extends AnnotationannotationTypeClass<?> valueType) {
             this. = annotationType;
             this. = valueType;
         }
        
        
Returns a hash code value for this key.

Returns:
A hash code value for this key.
        public int hashCode() {
            return .hashCode() ^ .hashCode();
        }
        
        
Determines if this key equals to the specified one.

Parameters:
obj Another key.
Returns:
true if this key equals to the specified one; false otherwise.
        public boolean equals(Object obj) {
            return ((CacheKeyobj). == 
                && ((CacheKeyobj). == ;
        }
        
    }
    
    
Adds validation component to the cache.

Parameters:
annotationType Annotation type.
validation Validation component to be cached.
    protected static void addToCache(Class<? extends AnnotationannotationType,
            Validation<?> validation) {
        addToCache(annotationTypevalidationvalidation.getType());
    }
    
    
Adds validation component to the cache.

Parameters:
annotationType Annotation type.
validation Validation component to be cached.
valueType The type of a value to be validated.
    protected static void addToCache(Class<? extends AnnotationannotationType,
            Validation<?> validationClass<?> valueType) {
        .put(new CacheKey(annotationTypevalueType), validation);
    }
    
    
Returns validation component from cache for the specified annotation and value types.

Parameters:
annotationType Annotation type.
valueType The type of a value to be validated.
Returns:
Validation component from cache for the specified annotation and value types or null if cache has no such entry.
    protected static Validation<?> getFromCache(Class<? extends AnnotationannotationType,
            Class<?> valueType) {
        return .get(new CacheKey(annotationTypevalueType));
    }
    
    // Factory methods
    
    
Creates a new instance of the specified validation component type.

The validation component implementation class can define constructor with any access modifier. The following are valid validation component constructors in descending order of priority:

  • Validation(Annotation, Class)
  • Validation(Class, Annotation)
  • Validation(Annotation)
  • Validation(Class)
  • Validation()
For example:
   DefaultValueConstraint(Class<V> type, DefaultValue annotation) {
       ...
   }
 

The returned validation component can be obtained from the cache if it was cached previously. Also this method automatically adds validation components to the cache if possible.

Parameters:
validationType Type of the validation component.
annotation Annotation of the validation component.
valueType Type of the value to be validated by the specified validation component type.
Returns:
A new or cached validation component instance.
Throws:
org.foxlabs.validation.ValidationTypeException if the specified value type is not supported by the specified validation component type.
org.foxlabs.validation.ValidationInstantiationException if validation component instantiation fails.
org.foxlabs.validation.ValidationSignatureException if validation component doesn't define valid constructor.
    protected static <T extends Validation<?>> T createValidation(Class<T> validationType,
            Annotation annotationClass<?> valueType) {
        Class<? extends AnnotationannotationType = annotation.annotationType();
        CacheKey key = new CacheKey(annotationTypevalueType);
        T validation = validationType.cast(.get(key));
        if (validation != null)
            return validation;
        
        Constructor<T> constructor;
        Object[] arguments;
        boolean cacheable = false;
        try {
            constructor = validationType.getDeclaredConstructor(annotationTypeClass.class);
            arguments = new Object[]{annotationvalueType};
        } catch (NoSuchMethodException e1) {
            try {
                constructor = validationType.getDeclaredConstructor(Class.classannotationType);
                arguments = new Object[]{valueTypeannotation};
            } catch (NoSuchMethodException e2) {
                try {
                    constructor = validationType.getDeclaredConstructor(annotationType);
                    arguments = new Object[]{annotation};
                } catch (NoSuchMethodException e3) {
                    try {
                        constructor = validationType.getDeclaredConstructor(Class.class);
                        arguments = new Object[]{valueType};
                        cacheable = true;
                    } catch (NoSuchMethodException e4) {
                        try {
                            constructor = validationType.getDeclaredConstructor();
                            arguments = new Object[]{};
                            cacheable = true;
                        } catch (NoSuchMethodException e5) {
                            throw new ValidationSignatureException(annotationvalidationType);
                        }
                    }
                }
            }
        }
        
        try {
            constructor.setAccessible(true);
            validation = constructor.newInstance(arguments);
            if (cacheable)
                .put(keyvalidation);
            return validationType.cast(validation);
        } catch (InvocationTargetException e) {
            Throwable cause = e.getTargetException();
            if (cause instanceof UnsupportedOperationException)
                throw new ValidationTypeException(annotationvalueType);
            throw new ValidationInstantiationException(annotationcause);
        } catch (IllegalAccessException e) {
            throw new ValidationInstantiationException(annotatione);
        } catch (InstantiationException e) {
            throw new ValidationInstantiationException(annotatione);
        }
    }
    
    // Annotation support
    
    
Default set of validation targets.
    protected static final Set<ValidationTargetDEFAULT_TARGET_SET =
            Collections.singleton(.);
    
    
Default set of constraint groups.
    protected static final Set<StringDEFAULT_GROUP_SET =
            Collections.singleton(.);
    
    
Returns array of annotations defined by the specified inner List annotation or array with single element which contains the specified annotation if it not conforms to the rules of annotation list definition.

Inner List annotation should define value property of an outer annotation array type.

Parameters:
annotation Annotation of the validation component.
Returns:
Array of annotations defined by the specified inner List annotation or array with single element which contains the specified annotation if it not conforms to the rules of annotation list definition.
    protected static Annotation[] getAnnotationList(Annotation annotation) {
        Annotation[] list = null;
        Class<? extends AnnotationannotationType = annotation.annotationType();
        if ("List".equals(annotationType.getSimpleName())) {
            Class<?> itemType = annotationType.getEnclosingClass();
            if (itemType != null && itemType.isAnnotation())
                list = (Annotation[]) getAnnotationProperty(annotation"value",
                        Types.arrayTypeOf(itemType));
        }
        return list == null ? new Annotation[]{annotation} : list;
    }
    
    
Returns message property value from the specified annotation or default annotation message if annotation has no such property.

Default annotation message should be defined as follows:

namespace + "." + annotation.annotationType().getSimpleName()
For example, default message for the org.foxlabs.validation.constraint.NotNull annotation and value namespace should be value.NotNull.

Parameters:
annotation Annotation of the validation component.
namespace Namespace of error message template key.
Returns:
message property value from the specified annotation or default annotation message if annotation has no such property.
    protected static String getAnnotationMessage(Annotation annotationString namespace) {
        String message = getAnnotationProperty(annotation"message"String.class);
        if (!(message == null || message.isEmpty()))
            return message;
        if (namespace == null || namespace.isEmpty())
            return null;
        return namespace + "." + annotation.annotationType().getSimpleName();
    }
    
    
Returns targets property value from the specified annotation or default target set if annotation has no such property.

Parameters:
annotation Annotation of the validation component.
Returns:
targets property value from the specified annotation or default target set if annotation has no such property.
    protected static Set<ValidationTargetgetAnnotationTargets(Annotation annotation) {
        ValidationTarget[] targets = getAnnotationProperty(annotation"targets",
                ValidationTarget[].class);
        if (targets == null || targets.length == 0)
            return ;
        Set<ValidationTargettargetSet = new HashSet<ValidationTarget>();
        for (ValidationTarget target : targets)
            targetSet.add(target);
        return targetSet;
    }
    
    
Returns groups property value from the specified annotation or the specified default groups if annotation has no such property and array of default groups is not empty; returns null otherwise.

Parameters:
annotation Annotation of the validation component.
defaults Array of default groups.
Returns:
groups property value from the specified annotation or the specified default groups if annotation has no such property and array of default groups is not empty; returns null otherwise.
    protected static String[] getAnnotationGroups(Annotation annotationString... defaults) {
        String[] groups = getAnnotationProperty(annotation"groups"String[].class);
        if (groups == null || groups.length == 0) {
            if (defaults == null || defaults.length == 0)
                return null;
            return defaults;
        }
        return groups;
    }
    
    
Returns annotation property value for the specified property name and type or null if the specified annotation has no property with such name and type.

Parameters:
annotation Annotation.
property Property name.
type Expected property type.
Returns:
Annotation property value for the specified property name and type or null if the specified annotation has no property with such name and type.
    protected static <T> T getAnnotationProperty(Annotation annotationString propertyClass<T> type) {
        try {
            Method method = annotation.annotationType().getMethod(property);
            Object value = method.invoke(annotation);
            return type == value.getClass() ? type.cast(value) : null;
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e.getTargetException());
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (ClassCastException e) {
            return null;
        } catch (NoSuchMethodException e) {
            return null;
        }
    }
    
    
Determines recursively if the specified annotation has an annotation of required type.

Parameters:
annotation Annotation to be tested.
requiredType Required annotation type.
Returns:
true if the specified annotation has an annotation of required type; false otherwise.
    protected static boolean isAnnotationPresent(Annotation annotation,
            Class<? extends AnnotationrequiredType) {
        return isAnnotationPresent(annotationrequiredTypenew HashSet<Class<?>>());
    }
    
    
Determines recursively if the specified annotation has an annotation of required type.

Parameters:
annotation Annotation to be tested.
requiredType Required annotation type.
checkedTypes Set of already checked types.
Returns:
true if the specified annotation has an annotation of required type; false otherwise.
    private static boolean isAnnotationPresent(Annotation annotation,
            Class<? extends AnnotationrequiredTypeSet<Class<?>> checkedTypes) {
        Class<? extends AnnotationannotationType = annotation.annotationType();
        if (annotationType.isAnnotationPresent(requiredType))
            return true;
        if (checkedTypes.contains(annotationType))
            return false;
        checkedTypes.add(annotationType);
        Annotation[] annotations = annotationType.getAnnotations();
        for (int i = 0; i < annotations.lengthi++) {
            Annotation[] list = getAnnotationList(annotations[i]);
            for (int j = 0; j < list.lengthj++)
                if (isAnnotationPresent(list[j], requiredTypecheckedTypes))
                    return true;
        }
        return false;
    }
    
New to GrepCode? Check out our FAQ X