WW-4694 WW-4472 Imroves annotation processing

This commit is contained in:
Lukasz Lenart
2017-02-13 11:55:11 +01:00
7 changed files with 148 additions and 29 deletions
@@ -207,7 +207,7 @@ public class DefaultWorkflowInterceptor extends MethodFilterInterceptor {
*/
protected String processInputConfig(final Object action, final String method, final String currentResultName) throws Exception {
String resultName = currentResultName;
InputConfig annotation = action.getClass().getMethod(method, EMPTY_CLASS_ARRAY).getAnnotation(InputConfig.class);
InputConfig annotation = AnnotationUtils.findAnnotation(action.getClass().getMethod(method, EMPTY_CLASS_ARRAY), InputConfig.class);
if (annotation != null) {
if (StringUtils.isNotEmpty(annotation.methodName())) {
Method m = action.getClass().getMethod(annotation.methodName());
@@ -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;
}
for( Class<? extends Annotation> c : annotation ){
if( annotatedElement.isAnnotationPresent(c) ) return true;
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;
}
return false;
/**
* 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;
}
}
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;
}
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;
}
/**
@@ -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));
}
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 testIsAnnotatedByWithSingleAnnotationArgMatchingReturnsTrue() throws Exception {
assertTrue(AnnotationUtils.isAnnotatedBy(DummyClass.class.getMethod("methodWithAnnotation"), MyAnnotation.class));
}
public void testGetAnnotatedMethodsIncludingSuperclassAndInterface() throws Exception {
@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));
Collection<? extends AnnotatedElement> ans = AnnotationUtils.getAnnotatedMethods(DummyClassExt.class, Deprecated.class, MyAnnotation.class, MyAnnotation2.class, MyAnnotationI.class);
assertEquals(3, ans.size());
}
@SuppressWarnings("unchecked")
@@ -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() {
}
}
@@ -5,5 +5,8 @@ public final class DummyClassExt extends DummyClass {
@MyAnnotation2
public void anotherAnnotatedMethod() {
}
@Override
public void methodWithAnnotation() {
}
}
@@ -0,0 +1,7 @@
package com.opensymphony.xwork2.util.annotation;
public interface DummyInterface {
@MyAnnotationI
public void interfaceMethodWithAnnotation();
}
@@ -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 {
}