package org.testng.internal.annotations;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.testng.ITestNGMethod;
import org.testng.annotations.IAnnotation;
import org.testng.annotations.IConfigurationAnnotation;
import org.testng.annotations.IDataProviderAnnotation;
import org.testng.annotations.IFactoryAnnotation;
import org.testng.annotations.IParametersAnnotation;
import org.testng.annotations.ITestAnnotation;
import org.testng.collections.Maps;
import org.testng.internal.ConstructorOrMethod;
import org.testng.internal.TestNGMethod;
import org.testng.internal.Utils;
import org.testng.internal.reflect.ReflectionHelper;
import org.testng.log4testng.Logger;
import org.testng.xml.XmlTest;
public class AnnotationHelper {
private static final Logger LOGGER = Logger.getLogger(AnnotationHelper.class);
private static final String SUFFIX = "Please check if all classes being referred to, in the annotation are available in the classpath.";
private static final List<Class<? extends IAnnotation>> ALL_ANNOTATIONS =
Arrays.asList(
ITestAnnotation.class,
IBeforeClass.class,
IAfterClass.class,
IBeforeMethod.class,
IAfterMethod.class,
IDataProviderAnnotation.class,
IFactoryAnnotation.class,
IParametersAnnotation.class,
IBeforeSuite.class,
IAfterSuite.class,
IBeforeTest.class,
IAfterTest.class,
IBeforeGroups.class,
IAfterGroups.class);
private static final List<Class<? extends IAnnotation>> CONFIGURATION_CLASSES =
Arrays.asList(
IBeforeSuite.class, IAfterSuite.class,
IBeforeTest.class, IAfterTest.class,
IBeforeGroups.class, IAfterGroups.class,
IBeforeClass.class, IAfterClass.class,
IBeforeMethod.class, IAfterMethod.class);
private AnnotationHelper() {
}
public static ITestAnnotation findTest(IAnnotationFinder finder, Class<?> cls) {
return finder.findAnnotation(cls, ITestAnnotation.class);
}
public static ITestAnnotation findTest(IAnnotationFinder finder, Method m) {
return finder.findAnnotation(m, ITestAnnotation.class);
}
public static ITestAnnotation findTest(IAnnotationFinder finder, ITestNGMethod m) {
return finder.findAnnotation(m, ITestAnnotation.class);
}
public static IFactoryAnnotation findFactory(IAnnotationFinder finder, Method m) {
return finder.findAnnotation(m, IFactoryAnnotation.class);
}
public static IFactoryAnnotation findFactory(IAnnotationFinder finder, Constructor c) {
return finder.findAnnotation(c, IFactoryAnnotation.class);
}
public static IConfigurationAnnotation findConfiguration(
IAnnotationFinder finder, ConstructorOrMethod m) {
IConfigurationAnnotation result = null;
IConfigurationAnnotation bs =
(IConfigurationAnnotation) finder.findAnnotation(m, IBeforeSuite.class);
IConfigurationAnnotation as =
(IConfigurationAnnotation) finder.findAnnotation(m, IAfterSuite.class);
IConfigurationAnnotation bt =
(IConfigurationAnnotation) finder.findAnnotation(m, IBeforeTest.class);
IConfigurationAnnotation at =
(IConfigurationAnnotation) finder.findAnnotation(m, IAfterTest.class);
IConfigurationAnnotation bg =
(IConfigurationAnnotation) finder.findAnnotation(m, IBeforeGroups.class);
IConfigurationAnnotation ag =
(IConfigurationAnnotation) finder.findAnnotation(m, IAfterGroups.class);
IConfigurationAnnotation bc =
(IConfigurationAnnotation) finder.findAnnotation(m, IBeforeClass.class);
IConfigurationAnnotation ac =
(IConfigurationAnnotation) finder.findAnnotation(m, IAfterClass.class);
IConfigurationAnnotation bm =
(IConfigurationAnnotation) finder.findAnnotation(m, IBeforeMethod.class);
IConfigurationAnnotation am =
(IConfigurationAnnotation) finder.findAnnotation(m, IAfterMethod.class);
if (bs != null
|| as != null
|| bt != null
|| at != null
|| bg != null
|| ag != null
|| bc != null
|| ac != null
|| bm != null
|| am != null) {
result = createConfiguration(bs, as, bt, at, bg, ag, bc, ac, bm, am);
}
return result;
}
public static IConfigurationAnnotation findConfiguration(IAnnotationFinder finder, Method m) {
return findConfiguration(finder, new ConstructorOrMethod(m));
}
private static IConfigurationAnnotation createConfiguration(
IConfigurationAnnotation bs,
IConfigurationAnnotation as,
IConfigurationAnnotation bt,
IConfigurationAnnotation at,
IConfigurationAnnotation bg,
IConfigurationAnnotation ag,
IConfigurationAnnotation bc,
IConfigurationAnnotation ac,
IConfigurationAnnotation bm,
IConfigurationAnnotation am) {
ConfigurationAnnotation result = new ConfigurationAnnotation();
if (bs != null) {
result.setBeforeSuite(true);
finishInitialize(result, bs);
}
if (as != null) {
result.setAfterSuite(true);
finishInitialize(result, as);
}
if (bt != null) {
result.setBeforeTest(true);
finishInitialize(result, bt);
}
if (at != null) {
result.setAfterTest(true);
finishInitialize(result, at);
}
if (bg != null) {
result.setBeforeGroups(bg.getBeforeGroups());
finishInitialize(result, bg);
}
if (ag != null) {
result.setAfterGroups(ag.getAfterGroups());
finishInitialize(result, ag);
}
if (bc != null) {
result.setBeforeTestClass(true);
finishInitialize(result, bc);
}
if (ac != null) {
result.setAfterTestClass(true);
finishInitialize(result, ac);
}
if (bm != null) {
result.setBeforeTestMethod(true);
finishInitialize(result, bm);
}
if (am != null) {
result.setAfterTestMethod(true);
finishInitialize(result, am);
}
return result;
}
private static void finishInitialize(
ConfigurationAnnotation result, IConfigurationAnnotation bs) {
result.setIsBeforeGroups(bs.isBeforeGroups());
result.setIsAfterGroups(bs.isAfterGroups());
result.setFakeConfiguration(true);
result.setAlwaysRun(bs.getAlwaysRun());
result.setDependsOnGroups(bs.getDependsOnGroups());
result.setDependsOnMethods(bs.getDependsOnMethods());
result.setDescription(bs.getDescription());
result.setEnabled(bs.getEnabled());
result.setGroups(bs.getGroups());
result.setInheritGroups(bs.getInheritGroups());
result.setTimeOut(bs.getTimeOut());
}
public static List<Class<? extends IAnnotation>> getAllAnnotations() {
return ALL_ANNOTATIONS;
}
public static ITestNGMethod[] findMethodsWithAnnotation(
Class<?> rootClass,
Class<? extends IAnnotation> annotationClass,
IAnnotationFinder annotationFinder,
XmlTest xmlTest) {
Map<String, ITestNGMethod> vResult = Maps.newHashMap();
try {
vResult = Maps.newHashMap();
Class<?> cls = rootClass;
while (null != cls) {
boolean hasClassAnnotation = isAnnotationPresent(annotationFinder, cls, annotationClass);
Method[] methods = ReflectionHelper.getLocalMethods(cls);
for (Method m : methods) {
boolean hasMethodAnnotation = isAnnotationPresent(annotationFinder, m, annotationClass);
boolean hasTestNGAnnotation =
isAnnotationPresent(annotationFinder, m, IFactoryAnnotation.class)
|| isAnnotationPresent(annotationFinder, m, ITestAnnotation.class)
|| isAnnotationPresent(annotationFinder, m);
boolean isPublic = Modifier.isPublic(m.getModifiers());
boolean isSynthetic = m.isSynthetic();
if ((isPublic && hasClassAnnotation && !isSynthetic && (!hasTestNGAnnotation))
|| hasMethodAnnotation) {
if (isAnnotationPresent(annotationFinder, m, IConfigurationAnnotation.class)
&& isAnnotationPresent(annotationFinder, cls, ITestAnnotation.class)) {
Utils.log(
"",
3,
"Method "
+ m
+ " has a configuration annotation"
+ " and a class-level @Test. This method will only be kept as a"
+ " configuration method.");
continue;
}
if (m.getReturnType() != void.class && !xmlTest.getAllowReturnValues()) {
Utils.log(
"",
2,
"Method "
+ m
+ " has a @Test annotation"
+ " but also a return value:"
+ " ignoring it. Use <suite allow-return-values=\"true\"> to fix this");
continue;
}
String key = createMethodKey(m);
if (null == vResult.get(key)) {
ITestNGMethod tm =
new TestNGMethod(
m, annotationFinder, xmlTest, null);
vResult.put(key, tm);
}
}
}
cls = cls.getSuperclass();
}
} catch (SecurityException e) {
LOGGER.error(e.getMessage(), e);
}
return vResult.values().toArray(new ITestNGMethod[0]);
}
public static <A extends Annotation> A findAnnotationSuperClasses(
Class<A> annotationClass, Class<?> parameterClass) {
Class<?> c = parameterClass;
while (c != null) {
A result = c.getAnnotation(annotationClass);
if (result != null) {
return result;
} else {
c = c.getSuperclass();
}
}
return null;
}
private static boolean isAnnotationPresent(
IAnnotationFinder annotationFinder,
Method m) {
for (Class<? extends IAnnotation> a : AnnotationHelper.CONFIGURATION_CLASSES) {
if (annotationFinder.findAnnotation(m, a) != null) {
return true;
}
}
return false;
}
private static boolean isAnnotationPresent(
IAnnotationFinder annotationFinder, Method m, Class<? extends IAnnotation> annotationClass) {
if (!annotationClass.equals(IConfigurationAnnotation.class)) {
return annotationFinder.findAnnotation(m, annotationClass) != null;
}
boolean found = false;
for (Class<? extends IAnnotation> clazz : CONFIGURATION_CLASSES) {
if (annotationFinder.findAnnotation(m, clazz) != null) {
found = true;
break;
}
}
return found;
}
private static boolean isAnnotationPresent(
IAnnotationFinder annotationFinder,
Class<?> cls,
Class<? extends IAnnotation> annotationClass) {
return annotationFinder.findAnnotation(cls, annotationClass) != null;
}
private static String createMethodKey(Method m) {
StringBuilder result = new StringBuilder(m.getName());
for (Class paramClass : m.getParameterTypes()) {
result.append(' ').append(paramClass.toString());
}
return result.toString();
}
static <A extends Annotation> A getAnnotationFromClass(Class<?> clazz, Class<A> a) {
Objects.requireNonNull(clazz, "Cannot retrieve annotations from a null class.");
Objects.requireNonNull(a, "Cannot work with a null annotation");
try {
return clazz.getAnnotation(a);
} catch (Throwable t) {
String msg = "Encountered problems when parsing the annotation on class " + clazz.getCanonicalName() + ". ";
msg += SUFFIX;
throw new TypeNotPresentException(msg, t);
}
}
static <A extends Annotation> A getAnnotationFromMethod(Method method, Class<A> a) {
Objects.requireNonNull(method, "Cannot retrieve annotations from a null method.");
Objects.requireNonNull(a, "Cannot work with a null annotation");
try {
return method.getAnnotation(a);
} catch (Throwable t) {
String mname = method.getDeclaringClass().getCanonicalName() + "." + method.getName();
String msg = "Encountered problems when parsing the annotation on method " + mname + "(). ";
msg += SUFFIX;
throw new TypeNotPresentException(msg, t);
}
}
static <A extends Annotation> A getAnnotationFromConstructor(Constructor<?> c, Class<A> a) {
Objects.requireNonNull(c, "Cannot retrieve annotations from a null constructor.");
Objects.requireNonNull(a, "Cannot work with a null annotation");
try {
return c.getAnnotation(a);
} catch (Throwable t) {
String msg = "Encountered problems when parsing the annotation on constructor " + c.getName() + ". ";
msg += SUFFIX;
throw new TypeNotPresentException(msg, t);
}
}
}