Home | History | Annotate | Download | only in dom
      1 package org.testng.xml.dom;
      2 
      3 import javax.xml.parsers.DocumentBuilder;
      4 import javax.xml.parsers.DocumentBuilderFactory;
      5 import javax.xml.parsers.ParserConfigurationException;
      6 import javax.xml.xpath.XPathExpressionException;
      7 
      8 import org.testng.Assert;
      9 import org.testng.collections.ListMultiMap;
     10 import org.testng.collections.Lists;
     11 import org.testng.collections.Maps;
     12 import org.testng.internal.collections.Pair;
     13 import org.testng.xml.XmlDefine;
     14 import org.testng.xml.XmlGroups;
     15 import org.testng.xml.XmlMethodSelector;
     16 import org.testng.xml.XmlSuite;
     17 import org.testng.xml.XmlTest;
     18 import org.w3c.dom.DOMException;
     19 import org.w3c.dom.Document;
     20 import org.w3c.dom.Element;
     21 import org.w3c.dom.Node;
     22 import org.w3c.dom.NodeList;
     23 import org.w3c.dom.Text;
     24 import org.xml.sax.SAXException;
     25 
     26 import java.io.File;
     27 import java.io.FileInputStream;
     28 import java.io.IOException;
     29 import java.lang.annotation.Annotation;
     30 import java.lang.reflect.InvocationTargetException;
     31 import java.lang.reflect.Method;
     32 import java.util.Arrays;
     33 import java.util.List;
     34 import java.util.Map;
     35 
     36 public 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 
    168 private 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