blob: c8213911f8cb1d46fb9e67d50b4cd9439eaaa061 [file] [log] [blame]
package org.testng.internal;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Map;
import org.testng.IClass;
import org.testng.TestNGException;
import org.testng.internal.annotations.IAnnotationFinder;
import org.testng.internal.annotations.IParameters;
import org.testng.xml.XmlTest;
/**
* This class/interface
*/
public class DefaultInstanceFactory implements IInstanceFactory {
public Object createInstance(
Class declaringClass,
Map<Class, IClass> classes,
XmlTest xmlTest,
IAnnotationFinder finder)
{
Object result = null;
try {
//
// Any annotated constructor?
//
Constructor constructor = findAnnotatedConstructor(finder, declaringClass);
if (null != constructor) {
IParameters annotation = (IParameters) finder.findAnnotation(constructor, IParameters.class);
String[] parameterNames = annotation.getValue();
Object[] parameters = Parameters.createParameters(constructor,
"@Parameters",
parameterNames,
xmlTest.getParameters(),
xmlTest.getSuite());
result = constructor.newInstance(parameters);
}
//
// No, just try to instantiate the parameterless constructor (or the one
// with a String)
//
else {
// If this class is a (non-static) nested class, the constructor contains a hidden
// parameter of the type of the enclosing class
Class[] parameterTypes = new Class[0];
Object[] parameters = new Object[0];
Class ec = getEnclosingClass(declaringClass);
boolean isStatic = 0 != (declaringClass.getModifiers() & Modifier.STATIC);
// Only add the extra parameter if the nested class is not static
if ((null != ec) && !isStatic) {
parameterTypes = new Class[] { ec };
// Create an instance of the enclosing class so we can instantiate
// the nested class (actually, we reuse the existing instance).
IClass enclosingIClass = classes.get(ec);
Object[] enclosingInstances = null;
if (null != enclosingIClass) {
enclosingInstances = enclosingIClass.getInstances(false);
if ((null == enclosingInstances) || (enclosingInstances.length == 0)) {
Object o = ec.newInstance();
enclosingIClass.addInstance(o);
enclosingInstances = new Object[] { o };
}
}
else {
enclosingInstances = new Object[] { ec.newInstance() };
}
Object enclosingClassInstance = enclosingInstances[0];
// Utils.createInstance(ec, classes, xmlTest, finder);
parameters = new Object[] { enclosingClassInstance };
} // isStatic
Constructor ct = declaringClass.getDeclaredConstructor(parameterTypes);
result = ct.newInstance(parameters);
}
}
catch (TestNGException ex) {
// We need to pass this along
throw ex;
}
catch (InvocationTargetException ex) {
throw new TestNGException("Cannot instantiate class " + declaringClass.getName(), ex);
}
catch (IllegalAccessException ex) {
result = tryOtherConstructor(declaringClass);
}
catch (NoSuchMethodException ex) {
result = tryOtherConstructor(declaringClass);
}
catch (InstantiationException ex) {
result = tryOtherConstructor(declaringClass);
}
catch (Throwable cause) {
// Something else went wrong when running the constructor
throw new TestNGException("An error occured while instantiating class " + declaringClass.getName(), cause);
}
return result;
}
/**
* Class.getEnclosingClass() only exists on JDK5, so reimplementing it
* here.
*/
private static Class getEnclosingClass(Class declaringClass) {
Class result = null;
String className = declaringClass.getName();
int index = className.indexOf("$");
if (index != -1) {
String ecn = className.substring(0, index);
try {
result = Class.forName(ecn);
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return result;
}
private static Object tryOtherConstructor(Class declaringClass) {
Object result = null;
try {
Constructor ctor = declaringClass.getConstructor(new Class[] { String.class });
result = ctor.newInstance(new Object[] { "Default test name" });
}
catch (Exception e) {
String message = e.getMessage();
if ((message == null) && (e.getCause() != null)) {
message = e.getCause().getMessage();
}
String error = "Could not create an instance of class " + declaringClass
+ ((message != null) ? (": " + message) : "")
+ ".\nPlease make sure it has a constructor that accepts either a String or no parameter.";
throw new TestNGException(error);
}
return result;
}
/**
* Find the best constructor given the parameters found on the annotation
*/
private static Constructor findAnnotatedConstructor(IAnnotationFinder finder, Class declaringClass) {
Constructor[] constructors = declaringClass.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
Constructor result = constructors[i];
IParameters annotation = (IParameters) finder.findAnnotation(result, IParameters.class);
if (null != annotation) {
String[] parameters = annotation.getValue();
Class[] parameterTypes = result.getParameterTypes();
if (parameters.length != parameterTypes.length) {
throw new TestNGException("Parameter count mismatch: " + result + "\naccepts "
+ parameterTypes.length
+ " parameters but the @Test annotation declares "
+ parameters.length);
}
else {
return result;
}
}
}
return null;
}
}