Repository: struts
Updated Branches:
  refs/heads/master 4b96958be -> 534dfc6bd


[WW-4694] annotation processing improved in order to navigate around
proxies, superclasses and interfaces

Project: http://git-wip-us.apache.org/repos/asf/struts/repo
Commit: http://git-wip-us.apache.org/repos/asf/struts/commit/c84b7967
Tree: http://git-wip-us.apache.org/repos/asf/struts/tree/c84b7967
Diff: http://git-wip-us.apache.org/repos/asf/struts/diff/c84b7967

Branch: refs/heads/master
Commit: c84b7967e9eb2f6307466b1644977423831d2ef1
Parents: 0023d96
Author: Yasser Zamani <yasser.zam...@live.com>
Authored: Sun Feb 5 17:54:28 2017 +0330
Committer: Yasser Zamani <yasser.zam...@live.com>
Committed: Sun Feb 5 17:54:28 2017 +0330

----------------------------------------------------------------------
 .../xwork2/util/AnnotationUtils.java            | 111 ++++++++++++++++---
 .../xwork2/util/AnnotationUtilsTest.java        |  35 ++++--
 .../xwork2/util/annotation/DummyClass.java      |   5 +-
 .../xwork2/util/annotation/DummyClassExt.java   |   5 +-
 .../xwork2/util/annotation/DummyInterface.java  |   7 ++
 .../xwork2/util/annotation/MyAnnotationI.java   |   8 ++
 6 files changed, 145 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/struts/blob/c84b7967/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java 
b/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java
index ef0ee53..1c37918 100644
--- a/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java
+++ b/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java
@@ -122,7 +122,14 @@ public class AnnotationUtils {
         Collection<Method> toReturn = new HashSet<>();
 
         for (Method m : clazz.getMethods()) {
-            if (ArrayUtils.isNotEmpty(annotation) && isAnnotatedBy(m, 
annotation)) {
+               boolean found = false;
+               for( Class<? extends Annotation> c : annotation ){
+                       if( null != findAnnotation(m, c) ){
+                               found =  true;
+                               break;
+                       }
+               }
+            if (found) {
                 toReturn.add(m);
             } else if (ArrayUtils.isEmpty(annotation) && 
ArrayUtils.isNotEmpty(m.getAnnotations())) {
                 toReturn.add(m);
@@ -133,22 +140,100 @@ public class AnnotationUtils {
        }
 
        /**
-        * Varargs version of 
<code>AnnotatedElement.isAnnotationPresent()</code>
-     * @param annotatedElement element to check
-     * @param annotation the {@link Annotation}s to find
-     * @return true is element is annotated by one of the annotation
-        * @see AnnotatedElement
+        * Find a single {@link Annotation} of {@code annotationType} from the 
supplied
+        * {@link Method}, traversing its super methods (i.e., from 
superclasses and
+        * interfaces) if no annotation can be found on the given method itself.
+        * <p>Annotations on methods are not inherited by default, so we need 
to handle
+        * this explicitly.
+        * @param method the method to look for annotations on
+        * @param annotationType the annotation type to look for
+        * @return the annotation found, or {@code null} if none
         */
-       public static boolean isAnnotatedBy(AnnotatedElement annotatedElement, 
Class<? extends Annotation>... annotation) {
-        if (ArrayUtils.isEmpty(annotation)) {
-            return false;
-        }
+       public static <A extends Annotation> A findAnnotation(Method method, 
Class<A> annotationType) {
+               A result = getAnnotation(method, annotationType);
+               Class<?> clazz = method.getDeclaringClass();
+               if (result == null) {
+                       result = searchOnInterfaces(method, annotationType, 
clazz.getInterfaces());
+               }
+               while (result == null) {
+                       clazz = clazz.getSuperclass();
+                       if (clazz == null || clazz.equals(Object.class)) {
+                               break;
+                       }
+                       try {
+                               Method equivalentMethod = 
clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
+                               result = getAnnotation(equivalentMethod, 
annotationType);
+                       }
+                       catch (NoSuchMethodException ex) {
+                               // No equivalent method found
+                       }
+                       if (result == null) {
+                               result = searchOnInterfaces(method, 
annotationType, clazz.getInterfaces());
+                       }
+               }
+               return result;
+       }
+
+       /**
+        * Get a single {@link Annotation} of {@code annotationType} from the 
supplied
+        * Method, Constructor or Field. Meta-annotations will be searched if 
the annotation
+        * is not declared locally on the supplied element.
+        * @param annotatedElement the Method, Constructor or Field from which 
to get the annotation
+        * @param annotationType the annotation type to look for, both locally 
and as a meta-annotation
+        * @return the matching annotation, or {@code null} if none found
+        */
+       public static <T extends Annotation> T getAnnotation(AnnotatedElement 
annotatedElement, Class<T> annotationType) {
+               try {
+                       T ann = annotatedElement.getAnnotation(annotationType);
+                       if (ann == null) {
+                               for (Annotation metaAnn : 
annotatedElement.getAnnotations()) {
+                                       ann = 
metaAnn.annotationType().getAnnotation(annotationType);
+                                       if (ann != null) {
+                                               break;
+                                       }
+                               }
+                       }
+                       return ann;
+               }
+               catch (Exception ex) {
+                       // Assuming nested Class values not resolvable within 
annotation attributes...
+                       return null;
+               }
+       }
 
-               for( Class<? extends Annotation> c : annotation ){
-                       if( annotatedElement.isAnnotationPresent(c) ) return 
true;
+       private static <A extends Annotation> A searchOnInterfaces(Method 
method, Class<A> annotationType, Class<?>... ifcs) {
+               A annotation = null;
+               for (Class<?> iface : ifcs) {
+                       if (isInterfaceWithAnnotatedMethods(iface)) {
+                               try {
+                                       Method equivalentMethod = 
iface.getMethod(method.getName(), method.getParameterTypes());
+                                       annotation = 
getAnnotation(equivalentMethod, annotationType);
+                               }
+                               catch (NoSuchMethodException ex) {
+                                       // Skip this interface - it doesn't 
have the method...
+                               }
+                               if (annotation != null) {
+                                       break;
+                               }
+                       }
                }
+               return annotation;
+       }
 
-               return false;
+       private static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {
+               boolean found = false;
+               for (Method ifcMethod : iface.getMethods()) {
+                       try {
+                               if (ifcMethod.getAnnotations().length > 0) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+                       catch (Exception ex) {
+                               // Assuming nested Class values not resolvable 
within annotation attributes...
+                       }
+               }
+               return found;
        }
 
     /**

http://git-wip-us.apache.org/repos/asf/struts/blob/c84b7967/core/src/test/java/com/opensymphony/xwork2/util/AnnotationUtilsTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/com/opensymphony/xwork2/util/AnnotationUtilsTest.java 
b/core/src/test/java/com/opensymphony/xwork2/util/AnnotationUtilsTest.java
index e59abe6..8ad4f35 100644
--- a/core/src/test/java/com/opensymphony/xwork2/util/AnnotationUtilsTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/util/AnnotationUtilsTest.java
@@ -5,8 +5,11 @@ import com.opensymphony.xwork2.util.annotation.DummyClass;
 import com.opensymphony.xwork2.util.annotation.DummyClassExt;
 import com.opensymphony.xwork2.util.annotation.MyAnnotation;
 import com.opensymphony.xwork2.util.annotation.MyAnnotation2;
+import com.opensymphony.xwork2.util.annotation.MyAnnotationI;
+
 import junit.framework.TestCase;
 
+import java.lang.annotation.Retention;
 import java.lang.reflect.AnnotatedElement;
 import java.util.Collection;
 
@@ -15,22 +18,32 @@ import java.util.Collection;
  */
 public class AnnotationUtilsTest extends TestCase {
 
-    @SuppressWarnings("unchecked")
-    public void testIsAnnotatedByWithoutAnnotationArgsReturnsFalse() throws 
Exception {
-        assertFalse(AnnotationUtils.isAnnotatedBy(DummyClass.class));
-        
assertFalse(AnnotationUtils.isAnnotatedBy(DummyClass.class.getMethod("methodWithAnnotation")));
+    public void testGetAnnotationMeta() throws Exception {
+        
assertNotNull(AnnotationUtils.getAnnotation(DummyClass.class.getMethod("methodWithAnnotation"),
 Retention.class));
     }
 
-    @SuppressWarnings("unchecked")
-    public void testIsAnnotatedByWithSingleAnnotationArgMatchingReturnsTrue() 
throws Exception {
-        
assertTrue(AnnotationUtils.isAnnotatedBy(DummyClass.class.getMethod("methodWithAnnotation"),
 MyAnnotation.class));
+    public void testGetAnnotation() throws Exception {
+        
assertNull(AnnotationUtils.getAnnotation(DummyClass.class.getMethod("methodWithAnnotation"),
 Deprecated.class));
+        
assertNotNull(AnnotationUtils.getAnnotation(DummyClass.class.getMethod("methodWithAnnotation"),
 MyAnnotation.class));
+    }
+
+    public void testFindAnnotationFromSuperclass() throws Exception {
+        
assertNotNull(AnnotationUtils.findAnnotation(DummyClassExt.class.getMethod("methodWithAnnotation"),
 MyAnnotation.class));
+    }
+
+    public void testFindAnnotationFromInterface() throws Exception {
+        
assertNotNull(AnnotationUtils.findAnnotation(DummyClass.class.getMethod("interfaceMethodWithAnnotation"),
 MyAnnotationI.class));
+    }
+
+    public void testFindAnnotation() throws Exception {
+        
assertNotNull(AnnotationUtils.findAnnotation(DummyClassExt.class.getMethod("anotherAnnotatedMethod"),
 MyAnnotation2.class));
     }
 
     @SuppressWarnings("unchecked")
-    public void testIsAnnotatedByWithMultiAnnotationArgMatchingReturnsTrue() 
throws Exception {
-        
assertFalse(AnnotationUtils.isAnnotatedBy(DummyClass.class.getMethod("methodWithAnnotation"),
 Deprecated.class));
-        
assertTrue(AnnotationUtils.isAnnotatedBy(DummyClass.class.getMethod("methodWithAnnotation"),
 MyAnnotation.class, Deprecated.class));
-        
assertTrue(AnnotationUtils.isAnnotatedBy(DummyClass.class.getMethod("methodWithAnnotation"),
 Deprecated.class, MyAnnotation.class));
+    public void testGetAnnotatedMethodsIncludingSuperclassAndInterface() 
throws Exception {
+
+        Collection<? extends AnnotatedElement> ans = 
AnnotationUtils.getAnnotatedMethods(DummyClassExt.class, Deprecated.class, 
MyAnnotation.class, MyAnnotation2.class, MyAnnotationI.class);
+        assertEquals(3, ans.size());
     }
 
     @SuppressWarnings("unchecked")

http://git-wip-us.apache.org/repos/asf/struts/blob/c84b7967/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClass.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClass.java 
b/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClass.java
index 22d6e07..da2f80e 100644
--- a/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClass.java
+++ b/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClass.java
@@ -1,7 +1,7 @@
 package com.opensymphony.xwork2.util.annotation;
 
 @MyAnnotation("class-test")
-public class DummyClass {
+public class DummyClass implements DummyInterface {
 
     public DummyClass() {
     }
@@ -10,4 +10,7 @@ public class DummyClass {
     public void methodWithAnnotation() {
     }
 
+       @Override
+       public void interfaceMethodWithAnnotation() {
+       }
 }

http://git-wip-us.apache.org/repos/asf/struts/blob/c84b7967/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClassExt.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClassExt.java 
b/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClassExt.java
index cfeebdf..7c024f6 100644
--- 
a/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClassExt.java
+++ 
b/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClassExt.java
@@ -5,5 +5,8 @@ public final class DummyClassExt extends DummyClass {
     @MyAnnotation2
     public void anotherAnnotatedMethod() {
     }
-
+    
+       @Override
+       public void methodWithAnnotation() {
+       }
 }

http://git-wip-us.apache.org/repos/asf/struts/blob/c84b7967/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyInterface.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyInterface.java
 
b/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyInterface.java
new file mode 100644
index 0000000..6faa3e4
--- /dev/null
+++ 
b/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyInterface.java
@@ -0,0 +1,7 @@
+package com.opensymphony.xwork2.util.annotation;
+
+public interface DummyInterface {
+       
+       @MyAnnotationI
+       public void interfaceMethodWithAnnotation();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/struts/blob/c84b7967/core/src/test/java/com/opensymphony/xwork2/util/annotation/MyAnnotationI.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/com/opensymphony/xwork2/util/annotation/MyAnnotationI.java 
b/core/src/test/java/com/opensymphony/xwork2/util/annotation/MyAnnotationI.java
new file mode 100644
index 0000000..ea866b9
--- /dev/null
+++ 
b/core/src/test/java/com/opensymphony/xwork2/util/annotation/MyAnnotationI.java
@@ -0,0 +1,8 @@
+package com.opensymphony.xwork2.util.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MyAnnotationI {
+}

Reply via email to