001: package org.testng.internal;
002:
003: import java.lang.reflect.Constructor;
004: import java.lang.reflect.Method;
005: import java.lang.reflect.Modifier;
006: import java.util.ArrayList;
007: import java.util.HashMap;
008: import java.util.List;
009: import java.util.Map;
010:
011: import org.testng.IClass;
012: import org.testng.IInstanceInfo;
013: import org.testng.IObjectFactory;
014: import org.testng.ITestContext;
015: import org.testng.TestNGException;
016: import org.testng.internal.annotations.AnnotationHelper;
017: import org.testng.internal.annotations.IAnnotation;
018: import org.testng.internal.annotations.IAnnotationFinder;
019: import org.testng.xml.XmlTest;
020:
021: /**
022: * This class creates an ITestClass from a test class.
023: *
024: * @author <a href="mailto:cedric@beust.com">Cedric Beust</a>
025: */
026: public class TestNGClassFinder extends BaseClassFinder {
027: private ITestContext m_testContext = null;
028: private Map<Class, List<Object>> m_instanceMap = new HashMap<Class, List<Object>>();
029:
030: public TestNGClassFinder(Class[] classes,
031: Map<Class, List<Object>> instanceMap, XmlTest xmlTest,
032: IAnnotationFinder annotationFinder, ITestContext testContext) {
033: m_testContext = testContext;
034:
035: if (null == instanceMap) {
036: instanceMap = new HashMap<Class, List<Object>>();
037: }
038:
039: //
040: // Find all the new classes and their corresponding instances
041: //
042: Class[] allClasses = classes;
043:
044: IObjectFactory objectFactory = testContext.getSuite()
045: .getObjectFactory();
046: //very first pass is to find ObjectFactory, can't create anything else until then
047: if (objectFactory == null) {
048: objectFactory = new ObjectFactoryImpl();
049: outer: for (Class cls : allClasses) {
050: for (Method m : cls.getMethods()) {
051: IAnnotation a = annotationFinder
052: .findAnnotation(
053: m,
054: org.testng.internal.annotations.IObjectFactory.class);
055: if (null != a) {
056: if (!org.testng.IObjectFactory.class
057: .isAssignableFrom(m.getReturnType())) {
058: throw new TestNGException("Return type of "
059: + m + " is not IObjectFactory");
060: }
061: try {
062: Object instance = cls.newInstance();
063: instanceMap.put(cls, java.util.Arrays
064: .asList(instance));
065: if (m.getParameterTypes().length > 0
066: && m.getParameterTypes()[0]
067: .equals(ITestContext.class)) {
068: objectFactory = (IObjectFactory) m
069: .invoke(instance, testContext);
070: } else {
071: objectFactory = (IObjectFactory) m
072: .invoke(instance);
073: }
074: break outer;
075: } catch (Exception ex) {
076: throw new TestNGException(
077: "Error creating object factory", ex);
078: }
079: }
080: }
081: }
082: }
083:
084: for (Class cls : allClasses) {
085: if ((null == cls)) {
086: ppp("FOUND NULL CLASS IN FOLLOWING ARRAY:");
087: int i = 0;
088: for (Class c : allClasses) {
089: ppp(" " + i + ": " + c);
090: }
091:
092: continue;
093: }
094:
095: if (isTestNGClass(cls, annotationFinder)) {
096: List allInstances = instanceMap.get(cls);
097: Object this Instance = (null != allInstances) ? allInstances
098: .get(0)
099: : null;
100:
101: // If annotation class and instances are abstract, skip them
102: if ((null == this Instance)
103: && Modifier.isAbstract(cls.getModifiers())) {
104: Utils.log("", 5,
105: "[WARN] Found an abstract class with no valid instance attached: "
106: + cls);
107: continue;
108: }
109:
110: IClass ic = findOrCreateIClass(cls, this Instance,
111: xmlTest, annotationFinder, objectFactory);
112: if (null != ic) {
113: Object[] theseInstances = ic.getInstances(false);
114: if (theseInstances.length == 0) {
115: theseInstances = ic.getInstances(true);
116: }
117: Object instance = theseInstances[0];
118: putIClass(cls, ic);
119:
120: Method factoryMethod = ClassHelper
121: .findFactoryMethod(cls, annotationFinder);
122: if (null != factoryMethod) {
123: FactoryMethod fm = new FactoryMethod( /* cls, */
124: factoryMethod, instance, xmlTest,
125: annotationFinder, m_testContext);
126: List<Class> moreClasses = new ArrayList<Class>();
127:
128: {
129: // ppp("INVOKING FACTORY " + fm + " " + this.hashCode());
130: Object[] instances = fm.invoke();
131:
132: //
133: // If the factory returned IInstanceInfo, get the class from it,
134: // otherwise, just call getClass() on the returned instances
135: //
136: if (instances.length > 0) {
137: Class elementClass = instances[0]
138: .getClass();
139: if (IInstanceInfo.class
140: .isAssignableFrom(elementClass)) {
141: for (Object o : instances) {
142: IInstanceInfo ii = (IInstanceInfo) o;
143: addInstance(ii
144: .getInstanceClass(), ii
145: .getInstance());
146: moreClasses.add(ii
147: .getInstanceClass());
148: }
149: } else {
150: for (Object o : instances) {
151: addInstance(o.getClass(), o);
152: if (!classExists(o.getClass())) {
153: moreClasses.add(o
154: .getClass());
155: }
156: }
157: }
158: }
159: }
160:
161: if (moreClasses.size() > 0) {
162: TestNGClassFinder finder = new TestNGClassFinder(
163: moreClasses
164: .toArray(new Class[moreClasses
165: .size()]),
166: m_instanceMap, xmlTest,
167: annotationFinder, m_testContext);
168:
169: IClass[] moreIClasses = finder
170: .findTestClasses();
171: for (IClass ic2 : moreIClasses) {
172: putIClass(ic2.getRealClass(), ic2);
173: }
174: } // if moreClasses.size() > 0
175: }
176: } // null != ic
177: } // if not TestNG class
178: else {
179: Utils.log("TestNGClassFinder", 3, "SKIPPING CLASS "
180: + cls + " no TestNG annotations found");
181: }
182: } // for
183:
184: //
185: // Add all the instances we found to their respective IClasses
186: //
187: for (Class c : m_instanceMap.keySet()) {
188: List<Object> instances = m_instanceMap.get(c);
189: for (Object instance : instances) {
190: IClass ic = getIClass(c);
191: if (null != ic) {
192: ic.addInstance(instance);
193: }
194: }
195: }
196: }
197:
198: /**
199: * Checks if class is a testng class based on the {@link IAnnotationFinder}
200: * passed in, which may be a jdk14 or jdk15 {@link IAnnotationFinder} instance.
201: * @param cls The class being tested
202: * @param annotationFinder The instance of annotation finder being used
203: * @return True if class has any testng annotations
204: */
205: public static boolean isTestNGClass(Class cls,
206: IAnnotationFinder annotationFinder) {
207: Class[] allAnnotations = AnnotationHelper.getAllAnnotations();
208:
209: for (Class annotation : allAnnotations) {
210: // Try on the methods
211: for (Method m : cls.getMethods()) {
212: IAnnotation ma = annotationFinder.findAnnotation(m,
213: annotation);
214: if (null != ma) {
215: return true;
216: }
217: }
218:
219: // Try on the class
220: IAnnotation a = annotationFinder.findAnnotation(cls,
221: annotation);
222: if (null != a) {
223: return true;
224: }
225:
226: // Try on the constructors
227: for (Constructor ctor : cls.getConstructors()) {
228: IAnnotation ca = annotationFinder.findAnnotation(ctor,
229: annotation);
230: if (null != ca) {
231: return true;
232: }
233: }
234: }
235:
236: return false;
237: }
238:
239: private void addInstance(Class clazz, Object o) {
240: List<Object> list = m_instanceMap.get(clazz);
241:
242: if (null == list) {
243: list = new ArrayList<Object>();
244: m_instanceMap.put(clazz, list);
245: }
246:
247: list.add(o);
248: }
249:
250: public static void ppp(String s) {
251: System.out.println("[TestNGClassFinder] " + s);
252: }
253:
254: }
|