1   /*
2    * The MIT License
3    *
4    * Copyright (c) <2010> <tap4j>
5    * 
6    * Permission is hereby granted, free of charge, to any person obtaining a copy
7    * of this software and associated documentation files (the "Software"), to deal
8    * in the Software without restriction, including without limitation the rights
9    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10   * copies of the Software, and to permit persons to whom the Software is
11   * furnished to do so, subject to the following conditions:
12   * 
13   * The above copyright notice and this permission notice shall be included in
14   * all copies or substantial portions of the Software.
15   * 
16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22   * THE SOFTWARE.
23   */
24  package org.tap4j.ext.testng;
25  
26  import java.io.File;
27  import java.io.FileInputStream;
28  import java.io.FileNotFoundException;
29  import java.io.InputStream;
30  import java.io.PrintWriter;
31  import java.io.StringWriter;
32  import java.text.SimpleDateFormat;
33  import java.util.Date;
34  import java.util.Iterator;
35  import java.util.LinkedHashMap;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Set;
39  import java.util.StringTokenizer;
40  
41  import org.apache.commons.lang.StringUtils;
42  import org.testng.ITestResult;
43  import org.testng.xml.XmlClass;
44  import org.testng.xml.XmlInclude;
45  import org.testng.xml.XmlPackage;
46  import org.testng.xml.XmlSuite;
47  import org.testng.xml.XmlTest;
48  import org.yaml.snakeyaml.DumperOptions.LineBreak;
49  import org.yaml.snakeyaml.JavaBeanLoader;
50  import org.yaml.snakeyaml.reader.UnicodeReader;
51  
52  /**
53   * TestNG YAMLish utility class.
54   * 
55   * @since 1.0
56   */
57  @SuppressWarnings("unchecked") // TODO: explain why this can be unchecked
58  public final class TestNGYAMLishUtils {
59      //TODO: add javadoc
60      /**
61       * 
62       */
63      private static final int EXTRA_SPACE = 3;
64  
65      /**
66       * Date Format used to format a datetime in ISO-8061 for YAMLish diagnostic.
67       */
68      public static final SimpleDateFormat ISO_8061_DATE_FORMAT = new SimpleDateFormat(
69                                                                                       "yyyy-MM-dd'T'HH:mm:ss");
70  
71      public static final String LINE_SEPARATOR = LineBreak.UNIX.getString();
72  
73      /**
74       * Default hidden constructor.
75       */
76      private TestNGYAMLishUtils() {
77          super();
78      }
79  
80      /**
81       * @param testNgTestResult
82       * @return Message value
83       */
84      public static String getMessage(ITestResult testNgTestResult) {
85          return "TestNG Test " + testNgTestResult.getName();
86      }
87  
88      /**
89       * @param testNgTestResult
90       * @return Severity value
91       */
92      public static String getSeverity(ITestResult testNgTestResult) {
93          String severity = "~";
94          if (testNgTestResult.getThrowable() != null) {
95              severity = "High";
96          }
97          return severity;
98      }
99  
100     /**
101      * @param testNgTestResult
102      * @return Source value
103      */
104     public static String getSource(ITestResult testNgTestResult) {
105         String source = testNgTestResult.getTestClass().getName() + "#" +
106                         testNgTestResult.getMethod().getMethodName();
107         return source;
108     }
109 
110     /**
111      * Retrieves the date of the TestNG Test Result start time.
112      * 
113      * @return Datetime value
114      */
115     public static String getDatetime(ITestResult testNgTestResult) {
116         long ms = testNgTestResult.getStartMillis();
117         Date date = new Date(ms);
118         return ISO_8061_DATE_FORMAT.format(date);
119     }
120 
121     /**
122      * @param testNgTestResult
123      * @return File value
124      */
125     public static String getFile(ITestResult testNgTestResult) {
126         return testNgTestResult.getTestClass().getName();
127     }
128 
129     /**
130      * @param testNgTestResult
131      * @return Line value
132      */
133     public static String getLine(ITestResult testNgTestResult) {
134         String line = "~";
135         Throwable testNGException = testNgTestResult.getThrowable();
136         if (testNGException != null) {
137             StringBuilder lookFor = new StringBuilder();
138             lookFor.append(testNgTestResult.getTestClass().getName());
139             lookFor.append('.');
140             lookFor.append(testNgTestResult.getMethod().getMethodName());
141             lookFor.append('(');
142             lookFor.append(testNgTestResult.getTestClass().getClass()
143                 .getSimpleName());
144             lookFor.append(".java:");
145 
146             StackTraceElement[] els = testNGException.getStackTrace();
147 
148             for (int i = 0; i < els.length; i++) {
149                 StackTraceElement el = els[i];
150                 line = getLineNumberFromExceptionTraceLine(el.toString(),
151                                                            lookFor.toString());
152                 if (line.equals("") == Boolean.FALSE) {
153                     break;
154                 }
155             }
156         }
157         return line;
158     }
159 
160     /**
161      * Get the error line number from the exception stack trace
162      * 
163      * @param exceptionTraceLine
164      * @param substrToSearch
165      * @return error line number
166      */
167     public static String getLineNumberFromExceptionTraceLine(String exceptionTraceLine,
168                                                              String substrToSearch) {
169         String lineNumber = "";
170         int index = exceptionTraceLine.indexOf(substrToSearch);
171         if (index >= 0) {
172             int length = substrToSearch.length() + index;
173             if (exceptionTraceLine.lastIndexOf(')') > length) {
174                 lineNumber = exceptionTraceLine
175                     .substring(length, exceptionTraceLine.lastIndexOf(')'));
176             }
177         }
178         return lineNumber;
179     }
180 
181     /**
182      * @param testNgTestResult
183      * @return Name value
184      */
185     public static String getName(ITestResult testNgTestResult) {
186         return testNgTestResult.getName();
187     }
188 
189     /**
190      * @param testNgTestResult
191      * @return Extensions value
192      */
193     public static Object getExtensions(ITestResult testNgTestResult) {
194         Object extensions = null;
195         Set<String> attributeNames = testNgTestResult.getAttributeNames();
196         Iterator<String> iterator = attributeNames.iterator();
197         if (iterator.hasNext()) {
198             extensions = new LinkedHashMap<String, Object>();
199             for (; iterator.hasNext();) {
200                 String attributeName = iterator.next();
201                 Object attributeValue = testNgTestResult
202                     .getAttribute(attributeName);
203                 ((Map<String, Object>) extensions).put(attributeName,
204                                                        attributeValue);
205             }
206         } else {
207             extensions = '~';
208         }
209         return extensions;
210     }
211 
212     /**
213      * @param testNgTestResult
214      * @return Expected value
215      */
216     public static String getExpected(ITestResult testNgTestResult) {
217         Throwable throwable = testNgTestResult.getThrowable();
218 
219         String expected = null;
220 
221         if (throwable != null) {
222             StringWriter sw = new StringWriter();
223             PrintWriter pw = new PrintWriter(sw);
224             throwable.printStackTrace(pw);
225 
226             String stringException = sw.toString();
227 
228             String expectedToken = "expected:";
229             String butWasToken = " but was:";
230             int index = stringException.indexOf(expectedToken);
231 
232             if (index > 0) {
233                 expected = stringException
234                     .substring(index + expectedToken.length(),
235                                stringException.lastIndexOf(butWasToken));
236                 index = stringException.indexOf(butWasToken);
237             }
238         }
239         return expected;
240     }
241 
242     /**
243      * @param testNgTestResult
244      * @return Actual value
245      */
246     public static String getActual(ITestResult testNgTestResult) {
247         Throwable throwable = testNgTestResult.getThrowable();
248 
249         String actual = "~";
250 
251         if (throwable != null) {
252             StringWriter sw = new StringWriter();
253             PrintWriter pw = new PrintWriter(sw);
254             throwable.printStackTrace(pw);
255 
256             String stringException = sw.toString();
257 
258             String expectedToken = "expected:";
259             String butWasToken = " but was:";
260             int index = stringException.indexOf(expectedToken);
261             if (index > 0) {
262                 index = stringException.indexOf(butWasToken);
263                 int eolIndex = stringException.indexOf(System
264                     .getProperty("line.separator"), index);
265                 actual = stringException
266                     .substring(index + butWasToken.length(), eolIndex);
267             }
268         }
269 
270         return actual;
271     }
272 
273     /**
274      * Returns YAMLish multi-line display entry.
275      * 
276      * @param testNgTestResult TestNG TestResult
277      * @return YAMLish multi-line
278      */
279     public static String getDisplay(ITestResult testNgTestResult) {
280 
281         StringBuilder displayBuffer = new StringBuilder();
282 
283         String expected = getExpected(testNgTestResult);
284         String actual = getActual(testNgTestResult);
285 
286         if (StringUtils.isNotEmpty(expected) && StringUtils.isNotEmpty(actual)) {
287             int expectedLength = expected.length();
288             int actualLength = actual.length();
289 
290             int greater = expectedLength;
291             if (actualLength > expectedLength) {
292                 greater = actualLength;
293             } else if ("Expected".length() > greater) {
294                 greater = "Expected".length();
295             }
296 
297             // Actual length plus the two spaces and an extra for next character
298             int greaterPlus3 = greater + EXTRA_SPACE;
299 
300             displayBuffer.append("+" + fill(greaterPlus3, '-') + "+" +
301                                  fill(greaterPlus3, '-') + "+");
302             displayBuffer.append(LINE_SEPARATOR);
303 
304             displayBuffer.append("+" + fill(greater, "Got") + "|" +
305                                  fill(greater, "Expected") + "+");
306             displayBuffer.append(LINE_SEPARATOR);
307 
308             displayBuffer.append("+" + fill(greaterPlus3, '-') + "+" +
309                                  fill(greaterPlus3, '-') + "+");
310             displayBuffer.append(LINE_SEPARATOR);
311 
312             displayBuffer.append("+" + fill(greater, actual) + "|" +
313                                  fill(greater, expected) + "+");
314             displayBuffer.append(LINE_SEPARATOR);
315 
316             displayBuffer.append("+" + fill(greaterPlus3, '-') + "+" +
317                                  fill(greaterPlus3, '-') + "+");
318 
319         } else {
320             displayBuffer.append('~');
321         }
322 
323         return displayBuffer.toString();
324 
325     }
326 
327     /**
328      * @param greater
329      * @return
330      */
331     private static String fill(int greater, char c) {
332         StringBuilder sb = new StringBuilder();
333         for (int i = 0; i < greater; ++i) {
334             sb.append(Character.toString(c));
335         }
336         return sb.toString();
337     }
338 
339     /**
340      * @param greater
341      * @return
342      */
343     private static String fill(int greater, String s) {
344         StringBuilder sb = new StringBuilder();
345         sb.append(' ');
346         sb.append(s);
347         int newgreater = greater - s.length();
348         sb.append(fill(newgreater + 1, ' '));
349         sb.append(' ');
350         return sb.toString();
351     }
352 
353     /**
354      * @param testNgTestResult
355      * @return Dump value
356      */
357     public static Object getDump(ITestResult testNgTestResult) {
358         Object returnObject = null;
359         // TBD: print names
360         Object[] parameters = testNgTestResult.getParameters();
361         if (parameters.length > 0) {
362             returnObject = new LinkedHashMap<String, Object>();
363             for (int i = 0; i < parameters.length; i++) {
364                 Object parameter = parameters[i];
365                 ((Map<String, Object>) returnObject).put("param" + (i + 1),
366                                                          parameter);
367             }
368         } else {
369             returnObject = "~";
370         }
371         return returnObject.toString();
372     }
373 
374     /**
375      * @param testNgTestResult
376      * @return Error value
377      */
378     public static String getError(ITestResult testNgTestResult) {
379         String error = "~";
380 
381         Throwable t = testNgTestResult.getThrowable();
382 
383         if (t != null) {
384             error = t.getMessage();
385         }
386 
387         return error;
388     }
389 
390     /**
391      * @param testNgTestResult
392      * @return Backtrace value
393      */
394     public static String getBacktrace(ITestResult testNgTestResult) {
395         StringBuilder stackTrace = new StringBuilder();
396 
397         Throwable throwable = testNgTestResult.getThrowable();
398 
399         if (throwable != null) {
400             StringWriter sw = new StringWriter();
401             PrintWriter pw = new PrintWriter(sw);
402             throwable.printStackTrace(pw);
403             String stackTraceString = sw.toString();
404             stackTraceString = stackTraceString.trim().replaceAll("\\r\\n",
405                                                                   "\n");
406 
407             StringTokenizer st = new StringTokenizer(stackTraceString,
408                                                      LINE_SEPARATOR);
409             while (st.hasMoreTokens()) {
410                 String stackTraceLine = st.nextToken();
411                 stackTrace.append(stackTraceLine);
412                 stackTrace.append(LINE_SEPARATOR);
413             }
414 
415         } else {
416             stackTrace.append('~');
417         }
418 
419         return stackTrace.toString();
420     }
421 
422     /*
423      * Methods from SnakeYAML project. See
424      * http://code.google.com/p/snakeyaml/wiki/TestingNG_YAML for more.
425      */
426 
427     public static StringBuilder toYaml(XmlSuite suite) {
428         StringBuilder result = new StringBuilder();
429 
430         maybeAdd(result, "name", suite.getName(), null);
431         maybeAdd(result, "junit", suite.isJUnit(), XmlSuite.DEFAULT_JUNIT);
432         maybeAdd(result, "verbose", suite.getVerbose(),
433                  XmlSuite.DEFAULT_VERBOSE);
434         maybeAdd(result, "threadCount", suite.getThreadCount(),
435                  XmlSuite.DEFAULT_THREAD_COUNT);
436         maybeAdd(result, "dataProviderThreadCount",
437                  suite.getDataProviderThreadCount(),
438                  XmlSuite.DEFAULT_DATA_PROVIDER_THREAD_COUNT);
439         maybeAdd(result, "timeOut", suite.getTimeOut(), null);
440         maybeAdd(result, "parallel", suite.getParallel(),
441                  XmlSuite.DEFAULT_PARALLEL);
442         maybeAdd(result, "skipFailedInvocationCounts",
443                  suite.skipFailedInvocationCounts(),
444                  XmlSuite.DEFAULT_SKIP_FAILED_INVOCATION_COUNTS);
445 
446         toYaml(result, "parameters", "", suite.getParameters());
447         toYaml(result, suite.getPackages());
448 
449         if (suite.getListeners().size() > 0) {
450             result.append("listeners:\n");
451             toYaml(result, "  ", suite.getListeners());
452         }
453 
454         if (suite.getPackages().size() > 0) {
455             result.append("packages:\n");
456             toYaml(result, suite.getPackages());
457         }
458         toYaml(result, "listeners", suite.getListeners());
459         if (suite.getTests().size() > 0) {
460             result.append("tests:\n");
461             for (XmlTest t : suite.getTests()) {
462                 toYaml(result, "  ", t);
463             }
464         }
465 
466         if (suite.getChildSuites().size() > 0) {
467             result.append("suite-files:\n");
468             toYaml(result, "  ", suite.getSuiteFiles());
469         }
470 
471         return result;
472     }
473 
474     /**
475      * @param sb
476      * @param key
477      * @param value
478      * @param def
479      */
480     private static void maybeAdd(StringBuilder sb, String key, Object value,
481                                  Object def) {
482         maybeAdd(sb, "", key, value, def);
483     }
484 
485     /**
486      * @param sb
487      * @param sp
488      * @param key
489      * @param value
490      * @param def
491      */
492     private static void maybeAdd(StringBuilder sb, String sp, String key,
493                                  Object value, Object def) {
494         if (value != null && !value.equals(def)) {
495             sb.append(sp).append(key).append(": ").append(value.toString())
496                 .append("\n");
497         }
498     }
499 
500     /**
501      * @param result
502      * @param sp
503      * @param t
504      */
505     private static void toYaml(StringBuilder result, String sp, XmlTest t) {
506         String sp2 = sp + "  ";
507         result.append(sp).append("- name: ").append(t.getName()).append("\n");
508 
509         maybeAdd(result, sp2, "junit", t.isJUnit(), XmlSuite.DEFAULT_JUNIT);
510         maybeAdd(result, sp2, "verbose", t.getVerbose(),
511                  XmlSuite.DEFAULT_VERBOSE);
512         maybeAdd(result, sp2, "timeOut", t.getTimeOut(), null);
513         maybeAdd(result, sp2, "parallel", t.getParallel(),
514                  XmlSuite.DEFAULT_PARALLEL);
515         maybeAdd(result, sp2, "skipFailedInvocationCounts",
516                  t.skipFailedInvocationCounts(),
517                  XmlSuite.DEFAULT_SKIP_FAILED_INVOCATION_COUNTS);
518 
519         maybeAdd(result, "preserveOrder", sp2, t.getPreserveOrder(), "false"); // TBD:
520                                                                                // is
521                                                                                // it
522                                                                                // the
523                                                                                // default
524                                                                                // value?
525 
526         toYaml(result, "parameters", sp2, t.getParameters());
527 
528         if (t.getIncludedGroups().size() > 0) {
529             result.append(sp2).append("includedGroups: [ ")
530                 .append(StringUtils.join(t.getIncludedGroups(), ","))
531                 .append(" ]\n");
532         }
533 
534         if (t.getExcludedGroups().size() > 0) {
535             result.append(sp2).append("excludedGroups: [ ")
536                 .append(StringUtils.join(t.getExcludedGroups(), ","))
537                 .append(" ]\n");
538         }
539 
540         Map<String, List<String>> mg = t.getMetaGroups();
541         if (mg.size() > 0) {
542             result.append(sp2).append("metaGroups: { ");
543             boolean first = true;
544             for (String group : mg.keySet()) {
545                 if (!first) {
546                     result.append(", ");
547                 }
548                 result.append(group).append(": [ ")
549                     .append(StringUtils.join(mg.get(group), ",")).append(" ] ");
550                 first = false;
551             }
552             result.append(" }\n");
553         }
554 
555         if (t.getXmlPackages().size() > 0) {
556             result.append(sp2).append("xmlPackages:\n");
557             for (XmlPackage xp : t.getXmlPackages()) {
558                 toYaml(result, sp2 + "  - ", xp);
559             }
560         }
561 
562         if (t.getXmlClasses().size() > 0) {
563             result.append(sp2).append("classes:\n");
564             for (XmlClass xc : t.getXmlClasses()) {
565                 toYaml(result, sp2 + "  ", xc);
566             }
567         }
568 
569         result.append("\n");
570     }
571 
572     /**
573      * @param result
574      * @param sp2
575      * @param xc
576      */
577     private static void toYaml(StringBuilder result, String sp2, XmlClass xc) {
578         List<XmlInclude> im = xc.getIncludedMethods();
579         List<String> em = xc.getExcludedMethods();
580         String name = im.size() > 0 || em.size() > 0 ? "name: " : "";
581 
582         result.append(sp2).append("- " + name).append(xc.getName())
583             .append("\n");
584         if (im.size() > 0) {
585             result.append(sp2 + "  includedMethods:\n");
586             for (XmlInclude xi : im) {
587                 toYaml(result, sp2 + "    ", xi);
588             }
589         }
590 
591         if (em.size() > 0) {
592             result.append(sp2 + "  excludedMethods:\n");
593             toYaml(result, sp2 + "    ", em);
594         }
595     }
596 
597     /**
598      * @param result
599      * @param sp2
600      * @param xi
601      */
602     private static void toYaml(StringBuilder result, String sp2, XmlInclude xi) {
603         result.append(sp2 + "- " + xi.getName()).append("\n");
604     }
605 
606     /**
607      * @param result
608      * @param sp
609      * @param strings
610      */
611     private static void toYaml(StringBuilder result, String sp,
612                                List<String> strings) {
613         for (String l : strings) {
614             result.append(sp).append("- ").append(l).append("\n");
615         }
616     }
617 
618     // private static final String SP = "  ";
619 
620     /**
621 	 * 
622 	 */
623     private static void toYaml(StringBuilder sb, List<XmlPackage> packages) {
624         if (packages.size() > 0) {
625             sb.append("packages:\n");
626             for (XmlPackage p : packages) {
627                 toYaml(sb, "  ", p);
628             }
629         }
630         for (XmlPackage p : packages) {
631             toYaml(sb, "  ", p);
632         }
633     }
634 
635     /**
636      * @param sb
637      * @param sp
638      * @param p
639      */
640     private static void toYaml(StringBuilder sb, String sp, XmlPackage p) {
641         sb.append(sp).append("name: ").append(p.getName()).append("\n");
642 
643         generateIncludeExclude(sb, sp, "includes", p.getInclude());
644         generateIncludeExclude(sb, sp, "excludes", p.getExclude());
645     }
646 
647     /**
648      * @param sb
649      * @param sp
650      * @param key
651      * @param includes
652      */
653     private static void generateIncludeExclude(StringBuilder sb, String sp,
654                                                String key, List<String> includes) {
655         if (includes.size() > 0) {
656             sb.append(sp).append("  ").append(key).append("\n");
657             for (String inc : includes) {
658                 sb.append(sp).append("    ").append(inc);
659             }
660         }
661     }
662 
663     /**
664      * @param map
665      * @param out
666      */
667     private static void mapToYaml(Map<String, String> map, StringBuilder out) {
668         if (map.size() > 0) {
669             out.append("{ ");
670             boolean first = true;
671             for (Map.Entry<String, String> e : map.entrySet()) {
672                 if (!first) {
673                     out.append(", ");
674                 }
675                 first = false;
676                 out.append(e.getKey() + ": " + e.getValue());
677             }
678             out.append(" }\n");
679         }
680     }
681 
682     /**
683      * @param sb
684      * @param key
685      * @param sp
686      * @param parameters
687      */
688     private static void toYaml(StringBuilder sb, String key, String sp,
689                                Map<String, String> parameters) {
690         if (parameters.size() > 0) {
691             sb.append(sp).append(key).append(": ");
692             mapToYaml(parameters, sb);
693         }
694     }
695 
696     /**
697      * @param suite
698      * @param name
699      * @param target
700      */
701     @SuppressWarnings({
702         "unused", "rawtypes"
703     })
704     private static void addToMap(Map suite, String name, Map target) {
705         List<Map<String, String>> parameters = (List<Map<String, String>>) suite
706             .get(name);
707         if (parameters != null) {
708             for (Map<String, String> parameter : parameters) {
709                 for (Map.Entry p : parameter.entrySet()) {
710                     target.put(p.getKey(), p.getValue().toString());
711                 }
712             }
713         }
714     }
715 
716     /**
717      * @param suite
718      * @param name
719      * @param target
720      */
721     @SuppressWarnings({
722         "unused", "rawtypes"
723     })
724     private static void addToList(Map suite, String name, List target) {
725         List<Map<String, String>> parameters = (List<Map<String, String>>) suite
726             .get(name);
727         if (parameters != null) {
728             for (Map<String, String> parameter : parameters) {
729                 for (Map.Entry p : parameter.entrySet()) {
730                     target.add(p.getValue().toString());
731                 }
732             }
733         }
734     }
735 
736     /**
737      * @param filePath
738      * @param is
739      * @return XMLSuite
740      * @throws FileNotFoundException
741      */
742     public static XmlSuite parse(String filePath, InputStream is)
743         throws FileNotFoundException {
744         JavaBeanLoader<XmlSuite> loader = new JavaBeanLoader<XmlSuite>(
745                                                                        XmlSuite.class);
746         if (is == null) {
747             is = new FileInputStream(new File(filePath));
748         }
749         XmlSuite result = loader.load(new UnicodeReader(is)); // UnicodeReader
750                                                               // used to
751                                                               // respect BOM
752         result.setFileName(filePath);
753         // DEBUG
754         // System.out.println("[Yaml] " + result.toXml());
755 
756         // Adjust XmlTest parents
757         for (XmlTest t : result.getTests()) {
758             t.setSuite(result);
759         }
760 
761         return result;
762     }
763 
764     // private static void setField( Object xml, Map<?, ?> map, String key,
765     // String methodName, Class<?> parameter )
766     // {
767     // Object o = map.get(key);
768     // if (o != null)
769     // {
770     // Method m;
771     // try
772     // {
773     // m = xml.getClass().getMethod(methodName, parameter);
774     // m.invoke(xml, o);
775     // } catch (Exception e)
776     // {
777     // e.printStackTrace();
778     // }
779     // }
780     // }
781 
782 }