Revision: 1031
          http://stripes.svn.sourceforge.net/stripes/?rev=1031&view=rev
Author:   fdaoud
Date:     2009-01-09 19:45:06 +0000 (Fri, 09 Jan 2009)

Log Message:
-----------
Implementation for STS-634 (refactoring of DefaultValidationMetadataProvider)

Modified Paths:
--------------
    
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultValidationMetadataProvider.java

Property Changed:
----------------
    trunk/


Property changes on: trunk
___________________________________________________________________
Modified: svn:ignore
   - .classpath
.project
.settings

   + 
.classpath
.project
.settings
dist


Modified: 
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultValidationMetadataProvider.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultValidationMetadataProvider.java
 2009-01-09 19:28:54 UTC (rev 1030)
+++ 
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultValidationMetadataProvider.java
 2009-01-09 19:45:06 UTC (rev 1031)
@@ -16,6 +16,8 @@
 
 import java.beans.Introspector;
 import java.beans.PropertyDescriptor;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -41,7 +43,7 @@
  * this implementation looks first at the property's read method (getter), 
then its write method
  * (setter), and finally at the field itself.
  * 
- * @author Ben Gunter
+ * @author Ben Gunter, Freddy Daoud
  * @since Stripes 1.5
  */
 public class DefaultValidationMetadataProvider implements 
ValidationMetadataProvider {
@@ -65,6 +67,7 @@
         Map<String, ValidationMetadata> meta = cache.get(beanType);
         if (meta == null) {
             meta = loadForClass(beanType);
+            logDebugMessageForConfiguredValidations(beanType, meta);
             cache.put(beanType, meta);
         }
 
@@ -89,6 +92,72 @@
      */
     protected Map<String, ValidationMetadata> loadForClass(Class<?> beanType) {
         Map<String, ValidationMetadata> meta = new HashMap<String, 
ValidationMetadata>();
+
+        @SuppressWarnings("unchecked")
+        Map<String, AnnotationInfo> annotationInfoMap =
+            getAnnotationInfoMap(beanType, Validate.class, 
ValidateNestedProperties.class);
+
+        for (String propertyName : annotationInfoMap.keySet()) {
+            AnnotationInfo annotationInfo = 
annotationInfoMap.get(propertyName);
+
+            // get the @Validate and/or @ValidateNestedProperties
+            Validate simple = annotationInfo.getAnnotation(Validate.class);
+            ValidateNestedProperties nested = 
annotationInfo.getAnnotation(ValidateNestedProperties.class);
+            Class<?> clazz = annotationInfo.getTargetClass();
+
+            // add to allow list if @Validate present
+            if (simple != null) {
+                if (simple.field() == null || "".equals(simple.field())) {
+                    meta.put(propertyName, new 
ValidationMetadata(propertyName, simple));
+                }
+                else {
+                    log.warn("Field name present in @Validate but should be 
omitted: ",
+                        clazz, ", property ", propertyName, ", given field 
name ",
+                        simple.field());
+                }
+            }
+
+            // add all sub-properties referenced in @ValidateNestedProperties
+            if (nested != null) {
+                Validate[] validates = nested.value();
+                if (validates != null) {
+                    for (Validate validate : validates) {
+                        if (validate.field() != null && 
!"".equals(validate.field())) {
+                            String fullName = propertyName + '.' + 
validate.field();
+                            if (meta.containsKey(fullName)) {
+                                log.warn("More than one nested @Validate with 
same field name: "
+                                    + validate.field() + " on property " + 
propertyName);
+                            }
+                            meta.put(fullName, new 
ValidationMetadata(fullName, validate));
+                        }
+                        else {
+                            log.warn("Field name missing from nested 
@Validate: ", clazz,
+                                ", property ", propertyName);
+                        }
+                    }
+                }
+            }
+        }
+
+        return Collections.unmodifiableMap(meta);
+    }
+
+    /**
+     * Looks at a class's properties, searching for the specified annotations 
on the properties
+     * (field, getter method, or setter method). An exception is thrown if 
annotations are found
+     * in more than one of those three places.
+     *
+     * @param beanType the class on which to look for annotations.
+     * @param annotationClasses the classes of the annotations for which to 
look for.
+     * @return a map of property names to AnnotationInfo objects, which 
contain the class on which
+     * the annotations were found (if any), and the annotation objects that 
correspond to the
+     * annotation classes.
+     */
+    protected Map<String, AnnotationInfo> getAnnotationInfoMap(Class<?> 
beanType,
+        Class<? extends Annotation>... annotationClasses)
+    {
+        Map<String, AnnotationInfo> annotationInfoMap = new HashMap<String, 
AnnotationInfo>();
+
         Set<String> seen = new HashSet<String>();
         try {
             for (Class<?> clazz = beanType; clazz != null; clazz = 
clazz.getSuperclass()) {
@@ -112,140 +181,23 @@
                     catch (NoSuchFieldException e) {
                     }
 
-                    boolean onAccessor = accessor != null
-                            && Modifier.isPublic(accessor.getModifiers())
-                            && accessor.getDeclaringClass().equals(clazz)
-                            && (accessor.isAnnotationPresent(Validate.class) 
|| accessor
-                                    
.isAnnotationPresent(ValidateNestedProperties.class));
-                    boolean onMutator = mutator != null
-                            && Modifier.isPublic(mutator.getModifiers())
-                            && mutator.getDeclaringClass().equals(clazz)
-                            && (mutator.isAnnotationPresent(Validate.class) || 
mutator
-                                    
.isAnnotationPresent(ValidateNestedProperties.class));
-                    boolean onField = field != null
-                            && !Modifier.isStatic(field.getModifiers())
-                            && field.getDeclaringClass().equals(clazz)
-                            && (field.isAnnotationPresent(Validate.class) || 
field
-                                    
.isAnnotationPresent(ValidateNestedProperties.class));
+                    // this method throws an exception if there are conflicts
+                    AnnotationInfo annotationInfo = getAnnotationInfo(clazz, 
propertyName,
+                        new PropertyWrapper[] {
+                            new PropertyWrapper(accessor),
+                            new PropertyWrapper(mutator),
+                            new PropertyWrapper(field),
+                        },
+                        annotationClasses);
 
-                    // I don't think George Boole would like this ...
-                    int count = 0;
-                    if (onAccessor) ++count;
-                    if (onMutator) ++count;
-                    if (onField) ++count;
-
-                    // must be 0 or 1
-                    if (count > 1) {
-                        StringBuilder buf = new StringBuilder(
-                                "There are conflicting @Validate and/or 
@ValidateNestedProperties annotations in ")
-                                .append(clazz)
-                                .append(". The following elements are 
improperly annotated for the '")
-                                .append(propertyName)
-                                .append("' property:\n");
-                        if (onAccessor) {
-                            boolean hasSimple = 
accessor.isAnnotationPresent(Validate.class);
-                            boolean hasNested = accessor
-                                    
.isAnnotationPresent(ValidateNestedProperties.class);
-                            buf.append("--> Getter method 
").append(accessor.getName()).append(
-                                    " is annotated with ");
-                            if (hasSimple)
-                                buf.append("@Validate");
-                            if (hasSimple && hasNested)
-                                buf.append(" and ");
-                            if (hasNested)
-                                buf.append("@ValidateNestedProperties");
-                            buf.append('\n');
-                        }
-                        if (onMutator) {
-                            boolean hasSimple = 
mutator.isAnnotationPresent(Validate.class);
-                            boolean hasNested = mutator
-                                    
.isAnnotationPresent(ValidateNestedProperties.class);
-                            buf.append("--> Setter method 
").append(mutator.getName()).append(
-                                    " is annotated with ");
-                            if (hasSimple)
-                                buf.append("@Validate");
-                            if (hasSimple && hasNested)
-                                buf.append(" and ");
-                            if (hasNested)
-                                buf.append("@ValidateNestedProperties");
-                            buf.append('\n');
-                        }
-                        if (onField) {
-                            boolean hasSimple = 
field.isAnnotationPresent(Validate.class);
-                            boolean hasNested = field
-                                    
.isAnnotationPresent(ValidateNestedProperties.class);
-                            buf.append("--> Field 
").append(field.getName()).append(
-                                    " is annotated with ");
-                            if (hasSimple)
-                                buf.append("@Validate");
-                            if (hasSimple && hasNested)
-                                buf.append(" and ");
-                            if (hasNested)
-                                buf.append("@ValidateNestedProperties");
-                            buf.append('\n');
-                        }
-                        throw new StripesRuntimeException(buf.toString());
-                    }
-
                     // after the conflict check, stop processing fields we've 
already seen
                     if (seen.contains(propertyName))
                         continue;
 
-                    // get the @Validate and/or @ValidateNestedProperties
-                    Validate simple;
-                    ValidateNestedProperties nested;
-                    if (onAccessor) {
-                        simple = accessor.getAnnotation(Validate.class);
-                        nested = 
accessor.getAnnotation(ValidateNestedProperties.class);
+                    if (annotationInfo.atLeastOneAnnotationFound()) {
+                        annotationInfoMap.put(propertyName, annotationInfo);
                         seen.add(propertyName);
                     }
-                    else if (onMutator) {
-                        simple = mutator.getAnnotation(Validate.class);
-                        nested = 
mutator.getAnnotation(ValidateNestedProperties.class);
-                        seen.add(propertyName);
-                    }
-                    else if (onField) {
-                        simple = field.getAnnotation(Validate.class);
-                        nested = 
field.getAnnotation(ValidateNestedProperties.class);
-                        seen.add(propertyName);
-                    }
-                    else {
-                        simple = null;
-                        nested = null;
-                    }
-
-                    // add to allow list if @Validate present
-                    if (simple != null) {
-                        if (simple.field() == null || 
"".equals(simple.field())) {
-                            meta.put(propertyName, new 
ValidationMetadata(propertyName, simple));
-                        }
-                        else {
-                            log.warn("Field name present in @Validate but 
should be omitted: ",
-                                    clazz, ", property ", propertyName, ", 
given field name ",
-                                    simple.field());
-                        }
-                    }
-
-                    // add all sub-properties referenced in 
@ValidateNestedProperties
-                    if (nested != null) {
-                        Validate[] validates = nested.value();
-                        if (validates != null) {
-                            for (Validate validate : validates) {
-                                if (validate.field() != null && 
!"".equals(validate.field())) {
-                                    String fullName = propertyName + '.' + 
validate.field();
-                                    if (meta.containsKey(fullName)) {
-                                        log.warn("More than one nested 
@Validate with same field name: "
-                                            + validate.field() + " on property 
" + propertyName);
-                                    }
-                                    meta.put(fullName, new 
ValidationMetadata(fullName, validate));
-                                }
-                                else {
-                                    log.warn("Field name missing from nested 
@Validate: ", clazz,
-                                            ", property ", propertyName);
-                                }
-                            }
-                        }
-                    }
                 }
             }
         }
@@ -259,8 +211,102 @@
             sre.setStackTrace(e.getStackTrace());
             throw sre;
         }
+        return annotationInfoMap;
+    }
 
-        // Print out a pretty debug message showing what validations got 
configured
+    /**
+     * Looks at a class's properties, searching for the specified annotations 
on the given property
+     * objects. An exception is thrown if annotations are found in more than 
one of the specified
+     * property accessors (normally field, getter method, and setter method).
+     *
+     * @param clazz the class on which to look for annotations.
+     * @param propertyName the name of the property.
+     * @param propertyWrappers the property accessors.
+     * @param annotationClasses the classes of the annotations for which to 
look for.
+     * @return an AnnotationInfo object, which contains the class on which the 
annotations were found
+     * (if any), and the annotation objects that correspond to the annotation 
classes.
+     */
+    protected AnnotationInfo getAnnotationInfo(Class<?> clazz, String 
propertyName,
+        PropertyWrapper[] propertyWrappers, Class<? extends Annotation>... 
annotationClasses)
+    {
+        AnnotationInfo annotationInfo = new AnnotationInfo(clazz);
+
+        Map<PropertyWrapper, Map<Class<? extends Annotation>, Annotation>> map 
=
+            new HashMap<PropertyWrapper, Map<Class<? extends Annotation>, 
Annotation>>();
+
+        for (PropertyWrapper property : propertyWrappers) {
+            Map<Class<? extends Annotation>, Annotation> annotationMap =
+                new HashMap<Class<? extends Annotation>, Annotation>();
+
+            for (Class<? extends Annotation> annotationClass : 
annotationClasses)  {
+                Annotation annotation = findAnnotation(clazz, property, 
annotationClass);
+                if (annotation != null) {
+                    annotationMap.put(annotationClass, annotation);
+                }
+            }
+            if (!annotationMap.isEmpty()) {
+                map.put(property, annotationMap);
+            }
+        }
+
+        // must be 0 or 1
+        if (map.size() > 1) {
+            StringBuilder buf = new StringBuilder(
+                "There are conflicting @Validate and/or 
@ValidateNestedProperties annotations in ")
+                .append(clazz)
+                .append(". The following elements are improperly annotated for 
the '")
+                .append(propertyName)
+                .append("' property:\n");
+
+            for (PropertyWrapper property : map.keySet()) {
+                Map<Class<? extends Annotation>, Annotation> annotationMap = 
map.get(property);
+
+                buf.append("--> ").append(property.getType()).append(' ')
+                   .append(property.getName()).append(" is annotated with ");
+
+                for (Class<?> cls : annotationMap.keySet()) {
+                    buf.append('@').append(cls.getSimpleName()).append(' ');
+                }
+                buf.append('\n');
+            }
+            throw new StripesRuntimeException(buf.toString());
+        }
+        if (!map.isEmpty()) {
+            
annotationInfo.setAnnotationMap(map.entrySet().iterator().next().getValue());
+        }
+        return annotationInfo;
+    }
+
+    /**
+     * Returns an annotation (or <code>null</code> if none is found) for the 
given property
+     * accessor of a class. The property object must not be <code>null</code>, 
must be declared on
+     * the class, must be public if it is a method, and must not be static if 
it is a field, for it
+     * to be considered eligible to having the annotation.
+     *
+     * @param clazz the class on which to look for the annotation.
+     * @param property the property accessor.
+     * @param annotationClass the class of the annotation to look for.
+     * @return the annotation object, or <code>null</code> if no annotation 
was found.
+     */
+    protected Annotation findAnnotation(Class<?> clazz, PropertyWrapper 
property,
+        Class<? extends Annotation> annotationClass)
+    {
+        AccessibleObject accessible = property.getAccessibleObject(); 
+        if (accessible != null
+            && property.getDeclaringClass().equals(clazz)
+            && ( (accessible.getClass().equals(Method.class) && 
Modifier.isPublic(property.getModifiers()))
+              || (accessible.getClass().equals(Field.class) && 
!Modifier.isStatic(property.getModifiers()))
+            ))
+        {
+            return accessible.getAnnotation(annotationClass);
+        }
+        return null;
+    }
+
+    /**
+     * Prints out a pretty debug message showing what validations got 
configured.
+     */
+    protected void logDebugMessageForConfiguredValidations(Class<?> beanType, 
Map<String, ValidationMetadata> meta) {
         StringBuilder builder = new StringBuilder(128);
         for (Map.Entry<String, ValidationMetadata> entry : meta.entrySet()) {
             if (builder.length() > 0) {
@@ -270,9 +316,77 @@
             builder.append("->");
             builder.append(entry.getValue());
         }
-        log.debug("Loaded validations for ActionBean ", 
beanType.getSimpleName(), ": ", builder
-                .length() > 0 ? builder : "<none>");
+        log.debug("Loaded validations for ActionBean ", 
beanType.getSimpleName(), ": ",
+            builder.length() > 0 ? builder : "<none>");
+    }
 
-        return Collections.unmodifiableMap(meta);
+    /**
+     * Contains the class on which the annotations were found (if any), and 
the annotation objects
+     * that correspond to the annotation classes.
+     */
+    protected class AnnotationInfo {
+        private Class<?> targetClass;
+        private Map<Class<? extends Annotation>, Annotation> annotationMap;
+
+        public AnnotationInfo(Class<?> targetClass) {
+            this.targetClass = targetClass;
+        }
+
+        public Class<?> getTargetClass() {
+            return targetClass;
+        }
+
+        public void setAnnotationMap(Map<Class<? extends Annotation>, 
Annotation> annotationMap) {
+            this.annotationMap = annotationMap;
+        }
+
+        @SuppressWarnings("unchecked")
+        public <T extends Annotation> T getAnnotation(Class<T> 
annotationClass) {
+            return (T) annotationMap.get(annotationClass);
+        }
+
+        public boolean atLeastOneAnnotationFound() {
+            return !(annotationMap == null || annotationMap.isEmpty());
+        }
     }
+
+    /**
+     * For some reason, methods common to both the Field and Method classes 
are not in their parent
+     * class, AccessibleObject, so this class works around that limitation.
+     */
+    protected class PropertyWrapper {
+        private Field field;
+        private Method method;
+        private String type;
+
+        public PropertyWrapper(Field field) {
+            this.field = field;
+            this.type = "Field";
+        }
+
+        public PropertyWrapper(Method method) {
+            this.method = method;
+            this.type = "Method";
+        }
+
+        public AccessibleObject getAccessibleObject() {
+            return field != null ? field : method;
+        }
+
+        public String getName() {
+            return field != null ? field.getName() : method.getName();
+        }
+
+        public Class<?> getDeclaringClass() {
+            return field != null ? field.getDeclaringClass() : 
method.getDeclaringClass();
+        }
+
+        public int getModifiers() {
+            return field != null ? field.getModifiers() : 
method.getModifiers();
+        }
+
+        public String getType() {
+            return type;
+        }
+    }
 }


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It is the best place to buy or sell services for
just about anything Open Source.
http://p.sf.net/sfu/Xq1LFB
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development

Reply via email to