• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.testng.xml;
2 
3 import static org.testng.internal.Utils.isStringBlank;
4 
5 import org.testng.ITestObjectFactory;
6 import org.testng.TestNGException;
7 import org.testng.collections.Lists;
8 import org.testng.collections.Maps;
9 import org.testng.internal.Utils;
10 import org.testng.log4testng.Logger;
11 import org.xml.sax.Attributes;
12 import org.xml.sax.InputSource;
13 import org.xml.sax.SAXException;
14 import org.xml.sax.SAXParseException;
15 import org.xml.sax.helpers.DefaultHandler;
16 
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Stack;
23 
24 /**
25  * Suite definition parser utility.
26  *
27  * @author Cedric Beust
28  * @author <a href='mailto:the_mindstorm@evolva.ro'>Alexandru Popescu</a>
29  */
30 public class TestNGContentHandler extends DefaultHandler {
31   private XmlSuite m_currentSuite = null;
32   private XmlTest m_currentTest = null;
33   private List<String> m_currentDefines = null;
34   private List<String> m_currentRuns = null;
35   private List<XmlClass> m_currentClasses = null;
36   private int m_currentTestIndex = 0;
37   private int m_currentClassIndex = 0;
38   private int m_currentIncludeIndex = 0;
39   private List<XmlPackage> m_currentPackages = null;
40   private XmlPackage m_currentPackage = null;
41   private List<XmlSuite> m_suites = Lists.newArrayList();
42   private List<String> m_currentIncludedGroups = null;
43   private List<String> m_currentExcludedGroups = null;
44   private Map<String, String> m_currentTestParameters = null;
45   private Map<String, String> m_currentSuiteParameters = null;
46   private Map<String, String> m_currentClassParameters = null;
47   private Include m_currentInclude;
48   private List<String> m_currentMetaGroup = null;
49   private String m_currentMetaGroupName;
50 
51   enum Location {
52     SUITE,
53     TEST,
54     CLASS,
55     INCLUDE,
56     EXCLUDE
57   }
58   private Stack<Location> m_locations = new Stack<>();
59 
60   private XmlClass m_currentClass = null;
61   private ArrayList<XmlInclude> m_currentIncludedMethods = null;
62   private List<String> m_currentExcludedMethods = null;
63   private ArrayList<XmlMethodSelector> m_currentSelectors = null;
64   private XmlMethodSelector m_currentSelector = null;
65   private String m_currentLanguage = null;
66   private String m_currentExpression = null;
67   private List<String> m_suiteFiles = Lists.newArrayList();
68   private boolean m_enabledTest;
69   private List<String> m_listeners;
70 
71   private String m_fileName;
72   private boolean m_loadClasses;
73   private boolean m_validate = false;
74   private boolean m_hasWarn = false;
75 
TestNGContentHandler(String fileName, boolean loadClasses)76   public TestNGContentHandler(String fileName, boolean loadClasses) {
77     m_fileName = fileName;
78     m_loadClasses = loadClasses;
79   }
80 
ppp(String s)81   static private void ppp(String s) {
82     System.out.println("[TestNGContentHandler] " + s);
83   }
84 
85   /*
86    * (non-Javadoc)
87    *
88    * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
89    *      java.lang.String)
90    */
91   @Override
resolveEntity(String systemId, String publicId)92   public InputSource resolveEntity(String systemId, String publicId)
93       throws IOException, SAXException {
94     InputSource result = null;
95     if (Parser.DEPRECATED_TESTNG_DTD_URL.equals(publicId)
96         || Parser.TESTNG_DTD_URL.equals(publicId)) {
97       m_validate = true;
98       InputStream is = getClass().getClassLoader().getResourceAsStream(Parser.TESTNG_DTD);
99       if (null == is) {
100         is = Thread.currentThread().getContextClassLoader().getResourceAsStream(Parser.TESTNG_DTD);
101         if (null == is) {
102           System.out.println("WARNING: couldn't find in classpath " + publicId
103               + "\n" + "Fetching it from the Web site.");
104           result = super.resolveEntity(systemId, publicId);
105         }
106         else {
107           result = new InputSource(is);
108         }
109       }
110       else {
111         result = new InputSource(is);
112       }
113     }
114     else {
115       result = super.resolveEntity(systemId, publicId);
116     }
117 
118     return result;
119   }
120 
121   /**
122    * Parse <suite-file>
123    */
xmlSuiteFile(boolean start, Attributes attributes)124   private void xmlSuiteFile(boolean start, Attributes attributes) {
125     if (start) {
126       String path = attributes.getValue("path");
127       pushLocation(Location.SUITE);
128       m_suiteFiles.add(path);
129     }
130     else {
131       m_currentSuite.setSuiteFiles(m_suiteFiles);
132       popLocation(Location.SUITE);
133     }
134   }
135 
136   /**
137    * Parse <suite>
138    */
xmlSuite(boolean start, Attributes attributes)139   private void xmlSuite(boolean start, Attributes attributes) {
140     if (start) {
141       pushLocation(Location.SUITE);
142       String name = attributes.getValue("name");
143       if (isStringBlank(name)) {
144         throw new TestNGException("The <suite> tag must define the name attribute");
145       }
146       m_currentSuite = new XmlSuite();
147       m_currentSuite.setFileName(m_fileName);
148       m_currentSuite.setName(name);
149       m_currentSuiteParameters = Maps.newHashMap();
150 
151       String verbose = attributes.getValue("verbose");
152       if (null != verbose) {
153         m_currentSuite.setVerbose(Integer.parseInt(verbose));
154       }
155       String jUnit = attributes.getValue("junit");
156       if (null != jUnit) {
157         m_currentSuite.setJUnit(Boolean.valueOf(jUnit));
158       }
159       String parallel = attributes.getValue("parallel");
160       if (parallel != null) {
161         XmlSuite.ParallelMode mode = XmlSuite.ParallelMode.getValidParallel(parallel);
162         if (mode != null) {
163           m_currentSuite.setParallel(mode);
164         } else {
165           Utils.log("Parser", 1, "[WARN] Unknown value of attribute 'parallel' at suite level: '" + parallel + "'.");
166         }
167       }
168       String parentModule = attributes.getValue("parent-module");
169       if (parentModule != null) {
170         m_currentSuite.setParentModule(parentModule);
171       }
172       String guiceStage = attributes.getValue("guice-stage");
173       if (guiceStage != null) {
174         m_currentSuite.setGuiceStage(guiceStage);
175       }
176       String configFailurePolicy = attributes.getValue("configfailurepolicy");
177       if (null != configFailurePolicy) {
178         if (XmlSuite.SKIP.equals(configFailurePolicy) || XmlSuite.CONTINUE.equals(configFailurePolicy)) {
179           m_currentSuite.setConfigFailurePolicy(configFailurePolicy);
180         }
181       }
182       String groupByInstances = attributes.getValue("group-by-instances");
183       if (groupByInstances!= null) {
184         m_currentSuite.setGroupByInstances(Boolean.valueOf(groupByInstances));
185       }
186       String skip = attributes.getValue("skipfailedinvocationcounts");
187       if (skip != null) {
188         m_currentSuite.setSkipFailedInvocationCounts(Boolean.valueOf(skip));
189       }
190       String threadCount = attributes.getValue("thread-count");
191       if (null != threadCount) {
192         m_currentSuite.setThreadCount(Integer.parseInt(threadCount));
193       }
194       String dataProviderThreadCount = attributes.getValue("data-provider-thread-count");
195       if (null != dataProviderThreadCount) {
196         m_currentSuite.setDataProviderThreadCount(Integer.parseInt(dataProviderThreadCount));
197       }
198       String timeOut = attributes.getValue("time-out");
199       if (null != timeOut) {
200         m_currentSuite.setTimeOut(timeOut);
201       }
202       String objectFactory = attributes.getValue("object-factory");
203       if (null != objectFactory && m_loadClasses) {
204         try {
205           m_currentSuite.setObjectFactory((ITestObjectFactory)Class.forName(objectFactory).newInstance());
206         }
207         catch(Exception e) {
208           Utils.log("Parser", 1, "[ERROR] Unable to create custom object factory '" + objectFactory + "' :" + e);
209         }
210       }
211       String preserveOrder = attributes.getValue("preserve-order");
212       if (preserveOrder != null) {
213         m_currentSuite.setPreserveOrder(preserveOrder);
214       }
215       String allowReturnValues = attributes.getValue("allow-return-values");
216       if (allowReturnValues != null) {
217         m_currentSuite.setAllowReturnValues(Boolean.valueOf(allowReturnValues));
218       }
219     }
220     else {
221       m_currentSuite.setParameters(m_currentSuiteParameters);
222       m_suites.add(m_currentSuite);
223       m_currentSuiteParameters = null;
224       popLocation(Location.SUITE);
225     }
226   }
227 
228   /**
229    * Parse <define>
230    */
xmlDefine(boolean start, Attributes attributes)231   private void xmlDefine(boolean start, Attributes attributes) {
232     if (start) {
233       String name = attributes.getValue("name");
234       m_currentDefines = Lists.newArrayList();
235       m_currentMetaGroup = Lists.newArrayList();
236       m_currentMetaGroupName = name;
237     }
238     else {
239       m_currentTest.addMetaGroup(m_currentMetaGroupName, m_currentMetaGroup);
240       m_currentDefines = null;
241     }
242   }
243 
244   /**
245    * Parse <script>
246    */
xmlScript(boolean start, Attributes attributes)247   private void xmlScript(boolean start, Attributes attributes) {
248     if (start) {
249 //      ppp("OPEN SCRIPT");
250       m_currentLanguage = attributes.getValue("language");
251       m_currentExpression = "";
252     }
253     else {
254 //      ppp("CLOSE SCRIPT:@@" + m_currentExpression + "@@");
255       m_currentSelector.setExpression(m_currentExpression);
256       m_currentSelector.setLanguage(m_currentLanguage);
257       if (m_locations.peek() == Location.TEST) {
258         m_currentTest.setBeanShellExpression(m_currentExpression);
259       }
260       m_currentLanguage = null;
261       m_currentExpression = null;
262     }
263   }
264 
265   /**
266    * Parse <test>
267    */
xmlTest(boolean start, Attributes attributes)268   private void xmlTest(boolean start, Attributes attributes) {
269     if (start) {
270       m_currentTest = new XmlTest(m_currentSuite, m_currentTestIndex++);
271       pushLocation(Location.TEST);
272       m_currentTestParameters = Maps.newHashMap();
273       final String testName= attributes.getValue("name");
274       if(isStringBlank(testName)) {
275         throw new TestNGException("The <test> tag must define the name attribute");
276       }
277       m_currentTest.setName(attributes.getValue("name"));
278       String verbose = attributes.getValue("verbose");
279       if (null != verbose) {
280         m_currentTest.setVerbose(Integer.parseInt(verbose));
281       }
282       String jUnit = attributes.getValue("junit");
283       if (null != jUnit) {
284         m_currentTest.setJUnit(Boolean.valueOf(jUnit));
285       }
286       String skip = attributes.getValue("skipfailedinvocationcounts");
287       if (skip != null) {
288         m_currentTest.setSkipFailedInvocationCounts(Boolean.valueOf(skip));
289       }
290       String groupByInstances = attributes.getValue("group-by-instances");
291       if (groupByInstances!= null) {
292         m_currentTest.setGroupByInstances(Boolean.valueOf(groupByInstances));
293       }
294       String preserveOrder = attributes.getValue("preserve-order");
295       if (preserveOrder != null) {
296         m_currentTest.setPreserveOrder(preserveOrder);
297       }
298       String parallel = attributes.getValue("parallel");
299       if (parallel != null) {
300         XmlSuite.ParallelMode mode = XmlSuite.ParallelMode.getValidParallel(parallel);
301         if (mode != null) {
302           m_currentTest.setParallel(mode);
303         } else {
304           Utils.log("Parser", 1, "[WARN] Unknown value of attribute 'parallel' for test '"
305             + m_currentTest.getName() + "': '" + parallel + "'");
306         }
307       }
308       String threadCount = attributes.getValue("thread-count");
309       if(null != threadCount) {
310         m_currentTest.setThreadCount(Integer.parseInt(threadCount));
311       }
312       String timeOut = attributes.getValue("time-out");
313       if (null != timeOut) {
314         m_currentTest.setTimeOut(Long.parseLong(timeOut));
315       }
316       m_enabledTest= true;
317       String enabledTestString = attributes.getValue("enabled");
318       if(null != enabledTestString) {
319         m_enabledTest = Boolean.valueOf(enabledTestString);
320       }
321     }
322     else {
323       if (null != m_currentTestParameters && m_currentTestParameters.size() > 0) {
324         m_currentTest.setParameters(m_currentTestParameters);
325       }
326       if (null != m_currentClasses) {
327         m_currentTest.setXmlClasses(m_currentClasses);
328       }
329       m_currentClasses = null;
330       m_currentTest = null;
331       m_currentTestParameters = null;
332       popLocation(Location.TEST);
333       if(!m_enabledTest) {
334         List<XmlTest> tests= m_currentSuite.getTests();
335         tests.remove(tests.size() - 1);
336       }
337     }
338   }
339 
340   /**
341    * Parse <classes>
342    */
xmlClasses(boolean start, Attributes attributes)343   public void xmlClasses(boolean start, Attributes attributes) {
344     if (start) {
345       m_currentClasses = Lists.newArrayList();
346       m_currentClassIndex = 0;
347     }
348     else {
349       m_currentTest.setXmlClasses(m_currentClasses);
350       m_currentClasses = null;
351     }
352   }
353 
354   /**
355    * Parse <listeners>
356    */
xmlListeners(boolean start, Attributes attributes)357   public void xmlListeners(boolean start, Attributes attributes) {
358     if (start) {
359       m_listeners = Lists.newArrayList();
360     }
361     else {
362       if (null != m_listeners) {
363         m_currentSuite.setListeners(m_listeners);
364         m_listeners = null;
365       }
366     }
367   }
368 
369   /**
370    * Parse <listener>
371    */
xmlListener(boolean start, Attributes attributes)372   public void xmlListener(boolean start, Attributes attributes) {
373     if (start) {
374       String listener = attributes.getValue("class-name");
375       m_listeners.add(listener);
376     }
377   }
378 
379   /**
380    * Parse <packages>
381    */
xmlPackages(boolean start, Attributes attributes)382   public void xmlPackages(boolean start, Attributes attributes) {
383     if (start) {
384       m_currentPackages = Lists.newArrayList();
385     }
386     else {
387       if (null != m_currentPackages) {
388         switch(m_locations.peek()) {
389           case TEST:
390             m_currentTest.setXmlPackages(m_currentPackages);
391             break;
392           case SUITE:
393             m_currentSuite.setXmlPackages(m_currentPackages);
394             break;
395           case CLASS:
396             throw new UnsupportedOperationException("CLASS");
397         }
398       }
399 
400       m_currentPackages = null;
401       m_currentPackage = null;
402     }
403   }
404 
405   /**
406    * Parse <method-selectors>
407    */
xmlMethodSelectors(boolean start, Attributes attributes)408   public void xmlMethodSelectors(boolean start, Attributes attributes) {
409     if (start) {
410       m_currentSelectors = new ArrayList<>();
411     }
412     else {
413       switch(m_locations.peek()) {
414         case TEST:
415           m_currentTest.setMethodSelectors(m_currentSelectors);
416           break;
417         default:
418           m_currentSuite.setMethodSelectors(m_currentSelectors);
419           break;
420       }
421 
422       m_currentSelectors = null;
423     }
424   }
425 
426   /**
427    * Parse <selector-class>
428    */
xmlSelectorClass(boolean start, Attributes attributes)429   public void xmlSelectorClass(boolean start, Attributes attributes) {
430     if (start) {
431       m_currentSelector.setName(attributes.getValue("name"));
432       String priority = attributes.getValue("priority");
433       if (priority == null) {
434         priority = "0";
435       }
436       m_currentSelector.setPriority(Integer.parseInt(priority));
437     }
438     else {
439       // do nothing
440     }
441   }
442 
443   /**
444    * Parse <method-selector>
445    */
xmlMethodSelector(boolean start, Attributes attributes)446   public void xmlMethodSelector(boolean start, Attributes attributes) {
447     if (start) {
448       m_currentSelector = new XmlMethodSelector();
449     }
450     else {
451       m_currentSelectors.add(m_currentSelector);
452       m_currentSelector = null;
453     }
454   }
455 
xmlMethod(boolean start, Attributes attributes)456   private void xmlMethod(boolean start, Attributes attributes) {
457     if (start) {
458       m_currentIncludedMethods = new ArrayList<>();
459       m_currentExcludedMethods = Lists.newArrayList();
460       m_currentIncludeIndex = 0;
461     }
462     else {
463       m_currentClass.setIncludedMethods(m_currentIncludedMethods);
464       m_currentClass.setExcludedMethods(m_currentExcludedMethods);
465       m_currentIncludedMethods = null;
466       m_currentExcludedMethods = null;
467     }
468   }
469 
470   /**
471    * Parse <run>
472    */
xmlRun(boolean start, Attributes attributes)473   public void xmlRun(boolean start, Attributes attributes) throws SAXException {
474     if (start) {
475       m_currentRuns = Lists.newArrayList();
476     }
477     else {
478       if (m_currentTest != null) {
479         m_currentTest.setIncludedGroups(m_currentIncludedGroups);
480         m_currentTest.setExcludedGroups(m_currentExcludedGroups);
481       } else {
482         m_currentSuite.setIncludedGroups(m_currentIncludedGroups);
483         m_currentSuite.setExcludedGroups(m_currentExcludedGroups);
484       }
485       m_currentRuns = null;
486     }
487   }
488 
489 
490   /**
491    * Parse <group>
492    */
xmlGroup(boolean start, Attributes attributes)493   public void xmlGroup(boolean start, Attributes attributes) throws SAXException {
494     if (start) {
495       m_currentTest.addXmlDependencyGroup(attributes.getValue("name"),
496           attributes.getValue("depends-on"));
497     }
498   }
499 
500   /**
501    * NOTE: I only invoke xml*methods (e.g. xmlSuite()) if I am acting on both
502    * the start and the end of the tag. This way I can keep the treatment of
503    * this tag in one place. If I am only doing something when the tag opens,
504    * the code is inlined below in the startElement() method.
505    */
506   @Override
startElement(String uri, String localName, String qName, Attributes attributes)507   public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
508     if (!m_validate && !m_hasWarn) {
509       Logger.getLogger(TestNGContentHandler.class).warn("It is strongly recommended to add " +
510               "\"<!DOCTYPE suite SYSTEM \"http://testng.org/testng-1.0.dtd\" >\" at the top of your file, " +
511               "otherwise TestNG may fail or not work as expected.");
512       m_hasWarn = true;
513     }
514     String name = attributes.getValue("name");
515 
516     // ppp("START ELEMENT uri:" + uri + " sName:" + localName + " qName:" + qName +
517     // " " + attributes);
518     if ("suite".equals(qName)) {
519       xmlSuite(true, attributes);
520     }
521     else if ("suite-file".equals(qName)) {
522       xmlSuiteFile(true, attributes);
523     }
524     else if ("test".equals(qName)) {
525       xmlTest(true, attributes);
526     }
527     else if ("script".equals(qName)) {
528       xmlScript(true, attributes);
529     }
530     else if ("method-selector".equals(qName)) {
531       xmlMethodSelector(true, attributes);
532     }
533     else if ("method-selectors".equals(qName)) {
534       xmlMethodSelectors(true, attributes);
535     }
536     else if ("selector-class".equals(qName)) {
537       xmlSelectorClass(true, attributes);
538     }
539     else if ("classes".equals(qName)) {
540       xmlClasses(true, attributes);
541     }
542     else if ("packages".equals(qName)) {
543       xmlPackages(true, attributes);
544     }
545     else if ("listeners".equals(qName)) {
546       xmlListeners(true, attributes);
547     }
548     else if ("listener".equals(qName)) {
549       xmlListener(true, attributes);
550     }
551     else if ("class".equals(qName)) {
552       // If m_currentClasses is null, the XML is invalid and SAX
553       // will complain, but in the meantime, dodge the NPE so SAX
554       // can finish parsing the file.
555       if (null != m_currentClasses) {
556         m_currentClass = new XmlClass(name, m_currentClassIndex++, m_loadClasses);
557         m_currentClass.setXmlTest(m_currentTest);
558         m_currentClassParameters = Maps.newHashMap();
559         m_currentClasses.add(m_currentClass);
560         pushLocation(Location.CLASS);
561       }
562     }
563     else if ("package".equals(qName)) {
564       if (null != m_currentPackages) {
565         m_currentPackage = new XmlPackage();
566         m_currentPackage.setName(name);
567         m_currentPackages.add(m_currentPackage);
568       }
569     }
570     else if ("define".equals(qName)) {
571       xmlDefine(true, attributes);
572     }
573     else if ("run".equals(qName)) {
574       xmlRun(true, attributes);
575     }
576     else if ("group".equals(qName)) {
577       xmlGroup(true, attributes);
578     }
579     else if ("groups".equals(qName)) {
580       m_currentIncludedGroups = Lists.newArrayList();
581       m_currentExcludedGroups = Lists.newArrayList();
582     }
583     else if ("methods".equals(qName)) {
584       xmlMethod(true, attributes);
585     }
586     else if ("include".equals(qName)) {
587       xmlInclude(true, attributes);
588     }
589     else if ("exclude".equals(qName)) {
590       xmlExclude(true, attributes);
591     }
592     else if ("parameter".equals(qName)) {
593       String value = expandValue(attributes.getValue("value"));
594       switch(m_locations.peek()) {
595         case TEST:
596           m_currentTestParameters.put(name, value);
597           break;
598         case SUITE:
599           m_currentSuiteParameters.put(name, value);
600           break;
601         case CLASS:
602           m_currentClassParameters.put(name, value);
603           break;
604         case INCLUDE:
605           m_currentInclude.parameters.put(name, value);
606           break;
607       }
608     }
609   }
610 
611   private static class Include {
612     String name;
613     String invocationNumbers;
614     String description;
615     Map<String, String> parameters = Maps.newHashMap();
616 
Include(String name, String numbers)617     public Include(String name, String numbers) {
618       this.name = name;
619       this.invocationNumbers = numbers;
620     }
621   }
622 
xmlInclude(boolean start, Attributes attributes)623   private void xmlInclude(boolean start, Attributes attributes) {
624     if (start) {
625       m_locations.push(Location.INCLUDE);
626       m_currentInclude = new Include(attributes.getValue("name"),
627           attributes.getValue("invocation-numbers"));
628     } else {
629       String name = m_currentInclude.name;
630       if (null != m_currentIncludedMethods) {
631         String in = m_currentInclude.invocationNumbers;
632         XmlInclude include;
633         if (!Utils.isStringEmpty(in)) {
634           include = new XmlInclude(name, stringToList(in), m_currentIncludeIndex++);
635         } else {
636           include = new XmlInclude(name, m_currentIncludeIndex++);
637         }
638         for (Map.Entry<String, String> entry : m_currentInclude.parameters.entrySet()) {
639           include.addParameter(entry.getKey(), entry.getValue());
640         }
641 
642         include.setDescription(m_currentInclude.description);
643         m_currentIncludedMethods.add(include);
644       }
645       else if (null != m_currentDefines) {
646         m_currentMetaGroup.add(name);
647       }
648       else if (null != m_currentRuns) {
649         m_currentIncludedGroups.add(name);
650       }
651       else if (null != m_currentPackage) {
652         m_currentPackage.getInclude().add(name);
653       }
654 
655       popLocation(Location.INCLUDE);
656       m_currentInclude = null;
657     }
658   }
659 
xmlExclude(boolean start, Attributes attributes)660   private void xmlExclude(boolean start, Attributes attributes) {
661     if (start) {
662       m_locations.push(Location.EXCLUDE);
663       String name = attributes.getValue("name");
664       if (null != m_currentExcludedMethods) {
665         m_currentExcludedMethods.add(name);
666       }
667       else if (null != m_currentRuns) {
668         m_currentExcludedGroups.add(name);
669       }
670       else if (null != m_currentPackage) {
671         m_currentPackage.getExclude().add(name);
672       }
673     } else {
674       popLocation(Location.EXCLUDE);
675     }
676   }
677 
pushLocation(Location l)678   private void pushLocation(Location l) {
679     m_locations.push(l);
680   }
681 
popLocation(Location location)682   private Location popLocation(Location location) {
683     return m_locations.pop();
684   }
685 
stringToList(String in)686   private List<Integer> stringToList(String in) {
687     String[] numbers = in.split(" ");
688     List<Integer> result = Lists.newArrayList();
689     for (String n : numbers) {
690       result.add(Integer.parseInt(n));
691     }
692     return result;
693   }
694 
695   @Override
endElement(String uri, String localName, String qName)696   public void endElement(String uri, String localName, String qName) throws SAXException {
697     if ("suite".equals(qName)) {
698       xmlSuite(false, null);
699     }
700     else if ("suite-file".equals(qName)) {
701       xmlSuiteFile(false, null);
702     }
703     else if ("test".equals(qName)) {
704       xmlTest(false, null);
705     }
706     else if ("define".equals(qName)) {
707       xmlDefine(false, null);
708     }
709     else if ("run".equals(qName)) {
710       xmlRun(false, null);
711     }
712     else if ("methods".equals(qName)) {
713       xmlMethod(false, null);
714     }
715     else if ("classes".equals(qName)) {
716       xmlClasses(false, null);
717     }
718     else if ("packages".equals(qName)) {
719       xmlPackages(false, null);
720     }
721     else if ("class".equals(qName)) {
722       m_currentClass.setParameters(m_currentClassParameters);
723       m_currentClassParameters = null;
724       popLocation(Location.CLASS);
725     }
726     else if ("listeners".equals(qName)) {
727       xmlListeners(false, null);
728     }
729     else if ("method-selector".equals(qName)) {
730       xmlMethodSelector(false, null);
731     }
732     else if ("method-selectors".equals(qName)) {
733       xmlMethodSelectors(false, null);
734     }
735     else if ("selector-class".equals(qName)) {
736       xmlSelectorClass(false, null);
737     }
738     else if ("script".equals(qName)) {
739       xmlScript(false, null);
740     }
741     else if ("packages".equals(qName)) {
742       xmlPackages(false, null);
743     }
744     else if ("include".equals(qName)) {
745       xmlInclude(false, null);
746     } else if ("exclude".equals(qName)){
747       xmlExclude(false, null);
748     }
749   }
750 
751   @Override
error(SAXParseException e)752   public void error(SAXParseException e) throws SAXException {
753     if (m_validate) {
754       throw e;
755     }
756   }
757 
areWhiteSpaces(char[] ch, int start, int length)758   private boolean areWhiteSpaces(char[] ch, int start, int length) {
759     for (int i = start; i < start + length; i++) {
760       char c = ch[i];
761       if (c != '\n' && c != '\t' && c != ' ') {
762         return false;
763       }
764     }
765 
766     return true;
767   }
768 
769   @Override
characters(char ch[], int start, int length)770   public void characters(char ch[], int start, int length) {
771     if (null != m_currentLanguage && ! areWhiteSpaces(ch, start, length)) {
772       m_currentExpression += new String(ch, start, length);
773     }
774   }
775 
getSuite()776   public XmlSuite getSuite() {
777     return m_currentSuite;
778   }
779 
expandValue(String value)780   private static String expandValue(String value)
781   {
782     StringBuffer result = null;
783     int startIndex = 0;
784     int endIndex = 0;
785     int startPosition = 0;
786     String property = null;
787     while ((startIndex = value.indexOf("${", startPosition)) > -1 && (endIndex = value.indexOf("}", startIndex + 3)) > -1) {
788       property = value.substring(startIndex + 2, endIndex);
789       if (result == null) {
790         result = new StringBuffer(value.substring(startPosition, startIndex));
791       } else {
792         result.append(value.substring(startPosition, startIndex));
793       }
794       String propertyValue = System.getProperty(property);
795       if (propertyValue == null) {
796         propertyValue = System.getenv(property);
797       }
798       if (propertyValue != null) {
799         result.append(propertyValue);
800       } else {
801         result.append("${");
802         result.append(property);
803         result.append("}");
804       }
805       startPosition = startIndex + 3 + property.length();
806     }
807     if (result != null) {
808       result.append(value.substring(startPosition));
809       return result.toString();
810     } else {
811       return value;
812     }
813   }
814 }
815