1package org.testng.xml.dom; 2 3import javax.xml.parsers.DocumentBuilder; 4import javax.xml.parsers.DocumentBuilderFactory; 5import javax.xml.parsers.ParserConfigurationException; 6import javax.xml.xpath.XPathExpressionException; 7 8import org.testng.Assert; 9import org.testng.collections.ListMultiMap; 10import org.testng.collections.Lists; 11import org.testng.collections.Maps; 12import org.testng.internal.collections.Pair; 13import org.testng.xml.XmlDefine; 14import org.testng.xml.XmlGroups; 15import org.testng.xml.XmlMethodSelector; 16import org.testng.xml.XmlSuite; 17import org.testng.xml.XmlTest; 18import org.w3c.dom.DOMException; 19import org.w3c.dom.Document; 20import org.w3c.dom.Element; 21import org.w3c.dom.Node; 22import org.w3c.dom.NodeList; 23import org.w3c.dom.Text; 24import org.xml.sax.SAXException; 25 26import java.io.File; 27import java.io.FileInputStream; 28import java.io.IOException; 29import java.lang.annotation.Annotation; 30import java.lang.reflect.InvocationTargetException; 31import java.lang.reflect.Method; 32import java.util.Arrays; 33import java.util.List; 34import java.util.Map; 35 36public class XDom { 37// private static Map<String, Class<?>> m_map = Maps.newHashMap(); 38 private Document m_document; 39 private ITagFactory m_tagFactory; 40 41 public XDom(ITagFactory tagFactory, Document document) 42 throws XPathExpressionException, 43 InstantiationException, IllegalAccessException { 44 m_tagFactory = tagFactory; 45 m_document = document; 46 } 47 48 public Object parse() throws XPathExpressionException, 49 InstantiationException, IllegalAccessException, SecurityException, 50 IllegalArgumentException, NoSuchMethodException, 51 InvocationTargetException { 52 Object result = null; 53 NodeList nodes = m_document.getChildNodes(); 54 for (int i = 0; i < nodes.getLength(); i++) { 55 Node item = nodes.item(i); 56 if (item.getAttributes() != null) { 57 String nodeName = item.getNodeName(); 58 59 System.out.println("Node name:" + nodeName); 60 Class<?> c = m_tagFactory.getClassForTag(nodeName); 61 if (c == null) { 62 throw new RuntimeException("No class found for tag " + nodeName); 63 } 64 65 result = c.newInstance(); 66 populateAttributes(item, result); 67 if (ITagSetter.class.isAssignableFrom(result.getClass())) { 68 throw new RuntimeException("TAG SETTER"); 69 } 70 populateChildren(item, result); 71 } 72 } 73 return result; 74 } 75 76 public void populateChildren(Node root, Object result) throws InstantiationException, 77 IllegalAccessException, XPathExpressionException, SecurityException, IllegalArgumentException, NoSuchMethodException, InvocationTargetException { 78 p("populateChildren: " + root.getLocalName()); 79 NodeList childNodes = root.getChildNodes(); 80 ListMultiMap<String, Object> children = Maps.newListMultiMap(); 81 for (int i = 0; i < childNodes.getLength(); i++) { 82 Node item = childNodes.item(i); 83 if (item.getAttributes() != null) { 84 String nodeName = item.getNodeName(); 85 if ("suite-files".equals(nodeName)) { 86 System.out.println("BREAK"); 87 } 88 89 Class<?> c = m_tagFactory.getClassForTag(nodeName); 90 if (c == null) { 91 System.out.println("Warning: No class found for tag " + nodeName); 92 boolean foundSetter = invokeOnSetter(result, (Element) item, nodeName, null); 93 System.out.println(" found setter:" + foundSetter); 94 } else { 95 Object object = instantiateElement(c, result); 96 if (ITagSetter.class.isAssignableFrom(object.getClass())) { 97 System.out.println("Tag setter:" + result); 98 ((ITagSetter) object).setProperty(nodeName, result, item); 99 } else { 100 children.put(nodeName, object); 101 populateAttributes(item, object); 102 populateContent(item, object); 103 } 104 boolean foundSetter = invokeOnSetter(result, (Element) item, nodeName, object); 105// setProperty(result, nodeName, object); 106 populateChildren(item, object); 107 } 108 109// boolean foundSetter = invokeOnSetter(result, (Element) item, nodeName); 110// if (! foundSetter) { 111// boolean foundListSetter = invokeOnListSetter(result, nodeName, item); 112// if (! foundListSetter) { 113// } 114// } 115 } 116 } 117// System.out.println("Found children:" + children); 118// for (String s : children.getKeys()) { 119// setCollectionProperty(result, s, children.get(s), object); 120// } 121 } 122 123 /** 124 * Try to find a @ParentSetter. If this fails, try to find a constructor that takes the parent as a parameter. 125 * If this fails, use the default constructor. 126 */ 127 private Object instantiateElement(Class<?> c, Object parent) 128 throws SecurityException, NoSuchMethodException, 129 IllegalArgumentException, InstantiationException, IllegalAccessException, 130 InvocationTargetException { 131 Object result = null; 132 Method m = findMethodAnnotatedWith(c, ParentSetter.class); 133 if (m != null) { 134 result = c.newInstance(); 135 m.invoke(result, parent); 136 } else { 137 try { 138 result = c.getConstructor(parent.getClass()).newInstance(parent); 139 } catch(NoSuchMethodException ex) { 140 result = c.newInstance(); 141 } 142 } 143 144 return result; 145 } 146 147// private List<Pair<Method, ? extends Annotation>> 148// findMethodsWithAnnotation(Class<?> c, Class<? extends Annotation> ac) { 149// List<Pair<Method, ? extends Annotation>> result = Lists.newArrayList(); 150// for (Method m : c.getMethods()) { 151// Annotation a = m.getAnnotation(ac); 152// if (a != null) { 153// result.add(Pair.of(m, a)); 154// } 155// } 156// return result; 157// } 158 159 private Method findMethodAnnotatedWith(Class<?> c, Class<? extends Annotation> annotation) { 160 for (Method m : c.getMethods()) { 161 if (m.getAnnotation(annotation) != null) { 162 return m; 163 } 164 } 165 return null; 166 } 167 168private void populateContent(Node item, Object object) { 169 for (int i = 0; i < item.getChildNodes().getLength(); i++) { 170 Node child = item.getChildNodes().item(i); 171 if (child instanceof Text) { 172 setText(object, (Text) child); 173 } 174 } 175 } 176 177 private void setText(Object bean, Text child) { 178 List<Pair<Method, Wrapper>> pairs = 179 Reflect.findMethodsWithAnnotation(bean.getClass(), TagContent.class, bean); 180 for (Pair<Method, Wrapper> pair : pairs) { 181 try { 182 pair.first().invoke(bean, child.getTextContent()); 183 } catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException | DOMException e) { 184 e.printStackTrace(); 185 } 186 } 187 } 188 189 private boolean invokeOnSetter(Object object, Element element, String nodeName, 190 Object bean) { 191 Pair<Method, Wrapper> pair = 192 Reflect.findSetterForTag(object.getClass(), nodeName, bean); 193 194 List<Object[]> allParameters = null; 195 if (pair != null) { 196 Method m = pair.first(); 197 try { 198 if (pair.second() != null) { 199 allParameters = pair.second().getParameters(element); 200 } else { 201 allParameters = Lists.newArrayList(); 202 allParameters.add(new Object[] { bean }); 203 } 204 205 for (Object[] p : allParameters) { 206 m.invoke(object, p); 207 } 208 return true; 209 } catch (IllegalArgumentException e) { 210 System.out.println("Parameters: " + allParameters); 211 e.printStackTrace(); 212 } catch (IllegalAccessException | InvocationTargetException e) { 213 e.printStackTrace(); 214 } 215 } 216 217 return false; 218 } 219 220 private void populateAttributes(Node node, Object object) throws XPathExpressionException { 221 for (int j = 0; j < node.getAttributes().getLength(); j++) { 222 Node item = node.getAttributes().item(j); 223 setProperty(object, item.getLocalName(), item.getNodeValue()); 224 } 225 } 226 227 private void setProperty(Object object, String name, Object value) { 228 Pair<Method, Wrapper> setter = Reflect.findSetterForTag(object.getClass(), name, 229 value); 230 231 if (setter != null) { 232 Method foundMethod = setter.first(); 233 try { 234 Class<?> type = foundMethod.getParameterTypes()[0]; 235 if (type == Boolean.class || type == boolean.class) { 236 foundMethod.invoke(object, Boolean.parseBoolean(value.toString())); 237 } else if (type == Integer.class || type == int.class) { 238 foundMethod.invoke(object, Integer.parseInt(value.toString())); 239 } else { 240 foundMethod.invoke(object, value); 241 } 242 } catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException e) { 243 e.printStackTrace(); 244 } 245 } else { 246 e("Couldn't find setter method for property" + name + " on " + object.getClass()); 247 } 248 } 249 250// private Method findSetter(Object object, String name) { 251// String methodName = toCamelCaseSetter(name); 252// Method foundMethod = null; 253// for (Method m : object.getClass().getDeclaredMethods()) { 254// if (m.getName().equals(methodName)) { 255// foundMethod = m; 256// break; 257// } 258// } 259// return foundMethod; 260// } 261 262 private void p(String string) { 263 System.out.println("[XDom] " + string); 264 } 265 266 private void e(String string) { 267 System.out.println("[XDom] [Error] " + string); 268 } 269 270 public static void main(String[] args) throws SAXException, IOException, 271 ParserConfigurationException, XPathExpressionException, 272 InstantiationException, IllegalAccessException, SecurityException, 273 IllegalArgumentException, NoSuchMethodException, 274 InvocationTargetException { 275 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 276 factory.setNamespaceAware(true); // never forget this! 277 DocumentBuilder builder = factory.newDocumentBuilder(); 278 FileInputStream inputStream = 279 new FileInputStream(new File(System.getProperty("user.home") 280 + "/java/testng/src/test/resources/testng-all.xml")); 281 Document doc = builder.parse(inputStream); 282 XmlSuite result = (XmlSuite) new XDom(new TestNGTagFactory(), doc).parse(); 283 284 test(result); 285 System.out.println(result.toXml()); 286 } 287 288 private static void test(XmlSuite s) { 289 Assert.assertEquals("TestNG", s.getName()); 290 Assert.assertEquals(s.getDataProviderThreadCount(), 3); 291 Assert.assertEquals(s.getThreadCount(), 2); 292 293 { 294 // method-selectors 295 List<XmlMethodSelector> selectors = s.getMethodSelectors(); 296 Assert.assertEquals(selectors.size(), 2); 297 XmlMethodSelector s1 = selectors.get(0); 298 Assert.assertEquals(s1.getLanguage(), "javascript"); 299 Assert.assertEquals(s1.getExpression(), "foo()"); 300 XmlMethodSelector s2 = selectors.get(1); 301 Assert.assertEquals(s2.getClassName(), "SelectorClass"); 302 Assert.assertEquals(s2.getPriority(), 3); 303 } 304 305 { 306 // child-suites 307 List<String> suiteFiles = s.getSuiteFiles(); 308 Assert.assertEquals(suiteFiles, Arrays.asList("./junit-suite.xml")); 309 } 310 311 { 312 // parameters 313 Map<String, String> p = s.getParameters(); 314 Assert.assertEquals(p.size(), 2); 315 Assert.assertEquals(p.get("suiteParameter"), "suiteParameterValue"); 316 Assert.assertEquals(p.get("first-name"), "Cedric"); 317 } 318 319 { 320 // run 321 Assert.assertEquals(s.getIncludedGroups(), Arrays.asList("includeThisGroup")); 322 Assert.assertEquals(s.getExcludedGroups(), Arrays.asList("excludeThisGroup")); 323 XmlGroups groups = s.getGroups(); 324 325 // define 326 List<XmlDefine> defines = groups.getDefines(); 327 Assert.assertEquals(defines.size(), 1); 328 XmlDefine define = defines.get(0); 329 Assert.assertEquals(define.getName(), "bigSuite"); 330 Assert.assertEquals(define.getIncludes(), Arrays.asList("suite1", "suite2")); 331 332 // packages 333 Assert.assertEquals(s.getPackageNames(), Arrays.asList("com.example1", "com.example2")); 334 335 // listeners 336 Assert.assertEquals(s.getListeners(), 337 Arrays.asList("com.beust.Listener1", "com.beust.Listener2")); 338 // dependencies 339 // only defined on test for now 340 } 341 342 { 343 // tests 344 Assert.assertEquals(s.getTests().size(), 3); 345 for (int i = 0; i < s.getTests().size(); i++) { 346 if ("Nopackage".equals(s.getTests().get(i).getName())) { 347 testNoPackage(s.getTests().get(i)); 348 } 349 } 350 } 351 } 352 353 private static void testNoPackage(XmlTest t) { 354 Assert.assertEquals(t.getThreadCount(), 42); 355 Assert.assertTrue(t.getAllowReturnValues()); 356 357 // define 358 Map<String, List<String>> metaGroups = t.getMetaGroups(); 359 Assert.assertEquals(metaGroups.get("evenodd"), Arrays.asList("even", "odd")); 360 361 // run 362 Assert.assertEquals(t.getIncludedGroups(), Arrays.asList("nopackage", "includeThisGroup")); 363 Assert.assertEquals(t.getExcludedGroups(), Arrays.asList("excludeThisGroup")); 364 365 // dependencies 366 Map<String, String> dg = t.getXmlDependencyGroups(); 367 Assert.assertEquals(dg.size(), 2); 368 Assert.assertEquals(dg.get("e"), "f"); 369 Assert.assertEquals(dg.get("g"), "h"); 370 } 371} 372